/*******************************************************************************
 * Copyright (c) 2007 - 2025 Maxprograms.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 1.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/org/documents/epl-v10.html
 *
 * Contributors:
 *     Maxprograms - initial API and implementation
 *******************************************************************************/

import { ChildProcessWithoutNullStreams, execFileSync, spawn } from "child_process";
import { BrowserWindow, ClientRequest, IpcMainEvent, Menu, MenuItem, Notification, Rectangle, Size, app, clipboard, dialog, ipcMain, nativeTheme, net, screen, session, shell } from "electron";
import { IncomingMessage } from "electron/main";
import { appendFileSync, existsSync, lstatSync, mkdirSync, readFileSync, readdirSync, rmSync, unlinkSync, writeFileSync } from "fs";
import { MTUtils } from "mtengines/dist";
import { XMLElement } from "typesxml";
import { Locations, Point } from "./locations";
import { MTManager } from "./mtManager";

export class Swordfish {

    static readonly path = require('path');

    static mainWindow: BrowserWindow;
    static preferencesWindow: BrowserWindow;
    static aboutWindow: BrowserWindow;
    static licensesWindow: BrowserWindow;
    static addMemoryWindow: BrowserWindow;
    static importTmxWindow: BrowserWindow;
    static importXliffWindow: BrowserWindow;
    static addProjectWindow: BrowserWindow;
    static editProjectWindow: BrowserWindow;
    static addFileWindow: BrowserWindow;
    static defaultLangsWindow: BrowserWindow;
    static spellingLangsWindow: BrowserWindow;
    static filterSegmentsWindow: BrowserWindow;
    static tagsWindow: BrowserWindow;
    static replaceTextWindow: BrowserWindow;
    static addGlossaryWindow: BrowserWindow;
    static importGlossaryWindow: BrowserWindow;
    static concordanceSearchWindow: BrowserWindow;
    static termSearchWindow: BrowserWindow;
    static iatePluginWindow: BrowserWindow;
    static termOnlinePluginWindow: BrowserWindow;
    	static addTermWindow: BrowserWindow;
	    static editTermWindow: BrowserWindow | null;
    static glossaryManagerWindow: BrowserWindow;
    static goToWindow: BrowserWindow;
    static sortSegmentsWindow: BrowserWindow;
    static changeCaseWindow: BrowserWindow;
    static applyTmWindow: BrowserWindow;
    static notesWindow: BrowserWindow;
    static addNoteWindow: BrowserWindow;
    static updatesWindow: BrowserWindow;
    static gettingStartedWindow: BrowserWindow;
    static serverSettingsWindow: BrowserWindow;
    static browseDatabasesWindow: BrowserWindow;
    static addXmlConfigurationWindow: BrowserWindow;
    static editXmlFilterWindow: BrowserWindow;
    static configElementWindow: BrowserWindow;
    static tagsAnalysisWindow: BrowserWindow;
    static spaceAnalysisWindow: BrowserWindow;
    static systemInfoWindow: BrowserWindow;
    static promptWindow: BrowserWindow;
    static aiAssistantWindow: BrowserWindow;
    static doubaoSettingsWindow: BrowserWindow;
    static doubaoWebSockets: Map<string, any> | undefined;
    static aiAssistantOriginalBounds: { x: number; y: number; width: number; height: number } | null = null;
    static aiSettingsWindow: BrowserWindow | null = null;
    static termExtractionWindow: BrowserWindow;

    javapath: string = Swordfish.path.join(app.getAppPath(), 'bin', 'java');

    static appHome: string = Swordfish.path.join(app.getPath('appData'), app.name);
    static iconPath: string = Swordfish.path.join(app.getAppPath(), 'icons', 'icon.png');

    static latestVersion: string;
    static downloadLink: string;
    static termExtractionProjectData: any = null;
    static termExtractionCancelled: boolean = false;
    static extractedTermsSet: Set<string> = new Set();

    static currentDefaults: Rectangle;
    static currentPreferences: Preferences = {
        theme: 'system',
        zoomFactor: '1.0',
        srcLang: 'none',
        tgtLang: 'none',
        projectsFolder: Swordfish.path.join(app.getPath('appData'), app.name, 'projects'),
        memoriesFolder: Swordfish.path.join(app.getPath('appData'), app.name, 'memories'),
        glossariesFolder: Swordfish.path.join(app.getPath('appData'), app.name, 'glossaries'),
        catalog: Swordfish.path.join(app.getAppPath(), 'catalog', 'catalog.xml'),
        srx: Swordfish.path.join(app.getAppPath(), 'srx', 'default.srx'),
        paragraphSegmentation: false,
        acceptUnconfirmed: false,
        fuzzyTermSearches: false,
        caseSensitiveSearches: false,
        caseSensitiveMatches: true,
        autoConfirm: false,
        google: {
            enabled: false,
            apiKey: '',
            srcLang: 'none',
            tgtLang: 'none'
        },
        azure: {
            enabled: false,
            apiKey: '',
            srcLang: 'none',
            tgtLang: 'none'
        },
        deepl: {
            enabled: false,
            apiKey: '',
            srcLang: 'none',
            tgtLang: 'none'
        },
        chatGpt: {
            enabled: false,
            apiKey: '',
            model: 'gpt-4o-mini',
            fixTags: false
        },
        anthropic: {
            enabled: false,
            apiKey: '',
            model: 'claude-3-7-sonnet-latest',
            fixTags: false
        },
        modernmt: {
            enabled: false,
            apiKey: '',
            srcLang: 'none',
            tgtLang: 'none'
        },
        spellchecker: {
            defaultEnglish: 'en-US',
            defaultPortuguese: 'pt-BR',
            defaultSpanish: 'es'
        },
        os: process.platform,
        showGuide: true,
        pageRows: 500,
        deepseek: {
            enabled: false,
            apiKey: '',
            model: 'deepseek-chat'
        },
        kimi: {
            enabled: false,
            apiKey: '',
            model: 'moonshot-v1-8k'
        },
        ollama: {
            enabled: false,
            apiKey: '',
            model: 'llama3.2',
            baseURL: 'http://localhost:11434'
        },
        doubao: {
            enabled: false,
            apiKey: '',
            model: 'your-doubao-model-id'
        },
    }

    static currentCss: string;
    static currentStatus: any;

    static selectedFiles: string[];
    static sortParams: any;
    static filterParams: any;
    static memoryParam: string;
    static notesParam: any;
    static notesEvent: IpcMainEvent;
    static concordanceMemories: string[];
    static glossaryParam: string;
    static messageParam: any;
    static projectParam: string;
    static remoteTmParams: any;
    static typeParam: string;
    static xmlFilter: string;
    static filterElement: any;
    static editedProject: Project;
    static activeProject: string;

    static htmlContent: string;
    static htmlTitle: string;
    static htmlId: number;

    static SUCCESS: string = 'Success';
    static LOADING: string = 'Loading';
    static COMPLETED: string = 'Completed';
    static ERROR: string = 'Error';
    static SAVING: string = 'Saving';
    static PROCESSING: string = 'Processing';

    static spellCheckerLanguages: string[];
    static selectionRequest: IpcMainEvent;
    static addConfigurationEvent: IpcMainEvent;

    ls: ChildProcessWithoutNullStreams;

    static locations: Locations;

    constructor() {
        if (!app.requestSingleInstanceLock()) {
            app.quit();
        } else if (Swordfish.mainWindow) {
            // Someone tried to run a second instance, we should focus our window.
            if (Swordfish.mainWindow.isMinimized()) {
                Swordfish.mainWindow.restore();
            }
            Swordfish.mainWindow.focus();
        }
        if (process.platform === 'linux') {
            app.commandLine.appendSwitch('gtk-version', '3');
        }
        if (process.platform === 'win32') {
            this.javapath = Swordfish.path.join(app.getAppPath(), 'bin', 'java.exe');
        }

        if (!existsSync(Swordfish.appHome)) {
            mkdirSync(Swordfish.appHome, { recursive: true });
        }

        this.ls = spawn(this.javapath, ['--module-path', 'lib', '-m', 'swordfish/com.maxprograms.swordfish.TmsServer', '-port', '8070'], { cwd: app.getAppPath(), windowsHide: true });
        this.ls.stdout.on('data', (data) => {
            console.log(`stdout: ${data}`);
        });
        this.ls.stderr.on('data', (data) => {
            console.error(`stderr: ${data}`);
        });
        execFileSync(this.javapath, ['--module-path', 'lib', '-m', 'swordfish/com.maxprograms.swordfish.CheckURL', 'http://localhost:8070/TMSServer'], { cwd: app.getAppPath(), windowsHide: true });

        this.loadDefaults();
        Swordfish.locations = new Locations(Swordfish.path.join(app.getPath('appData'), app.name, 'locations.json'));
        Swordfish.loadPreferences();

        app.on('ready', () => {
            Swordfish.createWindow();
            let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'index.html');
            let fileUrl: URL = new URL('file://' + filePath);
            Swordfish.mainWindow.loadURL(fileUrl.href);
            Swordfish.mainWindow.on('resize', () => {
                this.saveDefaults();
            });
            Swordfish.mainWindow.on('move', () => {
                this.saveDefaults();
            });
            Swordfish.mainWindow.once('ready-to-show', () => {
                Swordfish.mainWindow.setBounds(Swordfish.currentDefaults);
                Swordfish.mainWindow.show();
                Swordfish.startup();
            });
        });

        app.on('before-quit', (event: Electron.Event) => {
            if (!this.ls.killed) {
                event.preventDefault();
                this.stopServer();
            }
        });

        app.on('quit', () => {
            app.quit();
        });

        app.on('window-all-closed', () => {
            app.quit();
        });

        nativeTheme.on('updated', () => {
            let oldCss: string = Swordfish.currentCss;
            let dark: string = 'file://' + Swordfish.path.join(app.getAppPath(), 'css', 'dark.css');
            let light: string = 'file://' + Swordfish.path.join(app.getAppPath(), 'css', 'light.css');
            let highcontrast: string = 'file://' + Swordfish.path.join(app.getAppPath(), 'css', 'highcontrast.css');
            if (Swordfish.currentPreferences.theme === 'system') {
                if (nativeTheme.shouldUseDarkColors) {
                    Swordfish.currentCss = dark;
                } else {
                    Swordfish.currentCss = light;
                }
                if (nativeTheme.shouldUseHighContrastColors) {
                    Swordfish.currentCss = highcontrast;
                }
                let windows: BrowserWindow[] = BrowserWindow.getAllWindows();
                for (let window of windows) {
                    window.webContents.send('set-theme', Swordfish.currentCss);
                }
            }
            if ((oldCss === dark || oldCss === light) && Swordfish.currentCss === highcontrast) {
                Swordfish.deleteAllTags('#003e66;', '#ffffff');
            }
            if ((oldCss === highcontrast) && (Swordfish.currentCss === dark || Swordfish.currentCss === light)) {
                Swordfish.deleteAllTags('#009688', '#ffffff');
            }
        });
        ipcMain.on('get-rows-page', (event: IpcMainEvent) => {
            event.sender.send('set-rows-page', Swordfish.currentPreferences.pageRows);
        });
        ipcMain.on('get-projects', (event: IpcMainEvent) => {
            Swordfish.getProjects(event);
        });
        ipcMain.on('get-memories', (event: IpcMainEvent) => {
            Swordfish.getMemories(event);
        });
        ipcMain.on('show-add-file', () => {
            Swordfish.addFile();
        });
        ipcMain.on('show-add-project', () => {
            Swordfish.showAddProject();
        });
        ipcMain.on('show-edit-project', (event: IpcMainEvent, project: Project) => {
            Swordfish.showEditProject(project);
        });
        ipcMain.on('export-translations', (event: IpcMainEvent, arg: any) => {
            Swordfish.exportProjectTranslations(arg);
        });
        ipcMain.on('export-open-project', (event: IpcMainEvent, arg: any) => {
            Swordfish.exportOpenProject(arg);
        });
        ipcMain.on('get-theme', (event: IpcMainEvent) => {
            event.sender.send('set-theme', Swordfish.currentCss);
        });
        ipcMain.on('get-note-params', (event: IpcMainEvent) => {
            event.sender.send('note-params', Swordfish.notesParam);
        });
        ipcMain.on('set-height', (event: IpcMainEvent, arg: { window: string, width: number, height: number }) => {
            Swordfish.setHeight(arg);
        });
        ipcMain.on('close-serverSettings', () => {
            Swordfish.serverSettingsWindow.close();
        });
        ipcMain.on('browse-server', (event: IpcMainEvent, arg: any) => {
            Swordfish.connectToServer(arg);
        });
        ipcMain.on('get-databases', (event: IpcMainEvent) => {
            event.sender.send('set-databases', Swordfish.remoteTmParams);
        });
        ipcMain.on('show-server-settings', (event: IpcMainEvent, arg: any) => {
            Swordfish.showServerSettings(arg.type);
        });
        ipcMain.on('close-browseServer', () => {
            Swordfish.browseDatabasesWindow.close();
        });
        ipcMain.on('add-databases', (event: IpcMainEvent, arg: any) => {
            Swordfish.addDatabases(arg);
        });
        ipcMain.on('close-licenses', () => {
            Swordfish.licensesWindow.close();
        });
        ipcMain.on('save-preferences', (event: IpcMainEvent, arg: Preferences) => {
            Swordfish.savePreferences(arg);
        });
        ipcMain.on('save-languages', (event: IpcMainEvent, arg: any) => {
            Swordfish.savelanguages(arg);
        });
        ipcMain.on('close-addProject', () => {
            Swordfish.addProjectWindow.close();
        });
        ipcMain.on('close-editProject', () => {
            Swordfish.editProjectWindow.close();
        });
        ipcMain.on('close-addFile', () => {
            Swordfish.addFileWindow.close();
        });
        ipcMain.on('close-go-to', () => {
            if (Swordfish.goToWindow) {
                Swordfish.goToWindow.close();
            }
        });
        ipcMain.on('go-to-segment', (event: IpcMainEvent, arg: any) => {
            Swordfish.mainWindow.focus();
            Swordfish.mainWindow.webContents.send('open-segment', arg);
        });
        ipcMain.on('get-project-param', (event: IpcMainEvent) => {
            Swordfish.projectParam ? event.sender.send('set-project', Swordfish.projectParam) : event.preventDefault();
        });
        ipcMain.on('close-replaceText', () => {
            Swordfish.replaceTextWindow.close();
        });
        ipcMain.on('close-tags', () => {
            Swordfish.closeTagsWindow();
        });
        ipcMain.on('get-selected-file', (event: IpcMainEvent) => {
            Swordfish.setSelectedFile(event);
        });
        ipcMain.on('get-languages', (event: IpcMainEvent) => {
            this.getLanguages(event);
        });
        ipcMain.on('select-source-files', (event: IpcMainEvent) => {
            this.selectSourceFiles(event);
        });
        ipcMain.on('close-about', () => {
            Swordfish.aboutWindow.close();
        });
        ipcMain.on('system-info-clicked', () => {
            Swordfish.showSystemInfo();
        });
        ipcMain.on('close-systemInfo', () => {
            Swordfish.systemInfoWindow.close();
        });
        ipcMain.on('get-system-info', (event: IpcMainEvent) => {
            Swordfish.getSystemInformation(event);
        });
        ipcMain.on('licenses-clicked', () => {
            Swordfish.showLicenses({ from: 'about' });
        });
        ipcMain.on('get-source-files', (event: IpcMainEvent) => {
            Swordfish.getSelectedFiles(event);
        });
        ipcMain.on('get-project-data', (event: IpcMainEvent) => {
            event.sender.send('project-data', Swordfish.editedProject);
        });
        ipcMain.on('update-project', (event: IpcMainEvent, arg: any) => {
            Swordfish.updateProject(arg);
        });
        ipcMain.on('create-project', (event: IpcMainEvent, arg: any) => {
            Swordfish.createProject(arg);
        });
        ipcMain.on('remove-projects', (event: IpcMainEvent, arg: any) => {
            Swordfish.removeProjects(arg);
        });
        ipcMain.on('close-project', (event: IpcMainEvent, arg: any) => {
            Swordfish.closeProject(arg);
        });
        ipcMain.on('show-add-memory', () => {
            Swordfish.showAddMemory();
        });
        ipcMain.on('close-addMemory', () => {
            Swordfish.addMemoryWindow.close();
        });
        ipcMain.on('add-memory', (event: IpcMainEvent, arg: any) => {
            Swordfish.addMemory(arg);
        });
        ipcMain.on('show-add-glossary', () => {
            Swordfish.showAddGlossary();
        });
        ipcMain.on('add-glossary', (event: IpcMainEvent, arg: any) => {
            Swordfish.addGlossary(arg);
        });
        ipcMain.on('close-addGlossary', () => {
            Swordfish.addGlossaryWindow.close();
        });
        ipcMain.on('get-glossaries', (event: IpcMainEvent) => {
            Swordfish.getGlossaries(event);
        });
        
        // 添加日志转发处理器
        ipcMain.on('log-to-main', (event: IpcMainEvent, message: string) => {
            console.log('[渲染进程日志]', message);
        });
        
        ipcMain.on('remove-glossaries', (event: IpcMainEvent, arg: any) => {
            Swordfish.removeGlossaries(arg);
        });
        ipcMain.on('show-add-term', (event: IpcMainEvent, glossary: string) => {
            Swordfish.showAddTerm(glossary);
        });
        		ipcMain.on('close-addTerm', () => {
			Swordfish.addTermWindow.close();
		});
		ipcMain.on('close-editTerm', () => {
			if (Swordfish.editTermWindow && !Swordfish.editTermWindow.isDestroyed()) {
				Swordfish.editTermWindow.close();
			}
		});
        		ipcMain.on('add-to-glossary', (event: IpcMainEvent, arg: any) => {
			Swordfish.addToGlossary(arg);
		});
		ipcMain.on('update-term', (event: IpcMainEvent, arg: any) => {
			Swordfish.updateTerm(arg);
		});
		ipcMain.on('delete-term', (event: IpcMainEvent, arg: any) => {
			Swordfish.deleteTerm(arg);
		});
		        ipcMain.on('edit-term', (event: IpcMainEvent, arg: any) => {
            console.log('收到edit-term请求:', arg);
            Swordfish.showEditTerm(arg.glossary, arg.termId, arg.term);
        });

        // 豆包语音识别配置
        ipcMain.handle('get-doubao-config', () => {
            try {
                const configPath = Swordfish.path.join(app.getPath('userData'), 'doubao-config.json');
                if (existsSync(configPath)) {
                    const configData = readFileSync(configPath, 'utf8');
                    return JSON.parse(configData);
                }
                return {
                    appKey: '',
                    accessKey: '',
                    url: 'wss://openspeech.bytedance.com/api/v3/sauc/bigmodel',
                    resourceId: 'volc.bigasr.sauc.duration'
                };
            } catch (error) {
                console.error('读取豆包配置失败:', error);
                return null;
            }
        });

        ipcMain.handle('save-doubao-config', (event, config) => {
            try {
                const configPath = Swordfish.path.join(app.getPath('userData'), 'doubao-config.json');
                writeFileSync(configPath, JSON.stringify(config, null, 2));
                return { success: true };
            } catch (error: any) {
                console.error('保存豆包配置失败:', error);
                return { success: false, error: error.message };
            }
        });

        // 显示豆包设置窗口
        ipcMain.on('show-doubao-settings', () => {
            Swordfish.showDoubaoSettings();
        });

        ipcMain.on('close-doubao-settings', () => {
            if (Swordfish.doubaoSettingsWindow && !Swordfish.doubaoSettingsWindow.isDestroyed()) {
                Swordfish.doubaoSettingsWindow.close();
            }
        });

        // 豆包WebSocket连接测试
        ipcMain.handle('test-doubao-connection', async (event, config) => {
            try {
                const WebSocket = require('ws');
                const connectId = require('crypto').randomUUID();
                
                const ws = new WebSocket(config.url, {
                    headers: {
                        'X-Api-App-Key': config.appKey,
                        'X-Api-Access-Key': config.accessKey,
                        'X-Api-Resource-Id': config.resourceId,
                        'X-Api-Connect-Id': connectId
                    }
                });

                return new Promise((resolve) => {
                    const timeout = setTimeout(() => {
                        ws.close();
                        resolve({ success: false, error: '连接超时 (10秒)' });
                    }, 10000);

                    ws.on('open', () => {
                        clearTimeout(timeout);
                        ws.close();
                        resolve({ success: true, message: '连接成功！豆包语音识别服务可用。' });
                    });

                    ws.on('error', (error: any) => {
                        clearTimeout(timeout);
                        console.error('豆包WebSocket测试连接错误:', error);
                        resolve({ success: false, error: 'WebSocket连接失败，请检查网络和认证信息' });
                    });

                    ws.on('close', (code: number, reason: string) => {
                        clearTimeout(timeout);
                        if (code !== 1000) {
                            resolve({ success: false, error: `连接关闭 (code: ${code}, reason: ${reason})` });
                        }
                    });
                });
            } catch (error: any) {
                console.error('豆包连接测试失败:', error);
                return { success: false, error: error.message };
            }
        });

        // 请求麦克风权限处理器
        ipcMain.handle('request-microphone-permission', async () => {
            try {
                // 在macOS上，Electron会自动处理麦克风权限请求
                const { systemPreferences } = require('electron');
                
                if (process.platform === 'darwin') {
                    const microphoneStatus = systemPreferences.getMediaAccessStatus('microphone');
                    console.log('麦克风权限状态:', microphoneStatus);
                    
                    if (microphoneStatus === 'denied') {
                        return { 
                            success: false, 
                            error: '麦克风权限被拒绝。请在macOS系统偏好设置 > 安全性与隐私 > 隐私 > 麦克风中，勾选Electron或Swordfish，然后重启应用。' 
                        };
                    } else if (microphoneStatus === 'restricted') {
                        return { 
                            success: false, 
                            error: '麦克风权限受限。请检查macOS的家长控制设置。' 
                        };
                    } else if (microphoneStatus === 'not-determined') {
                        // 尝试请求权限
                        const granted = await systemPreferences.askForMediaAccess('microphone');
                        return { 
                            success: granted, 
                            error: granted ? null : '用户拒绝了麦克风权限请求。' 
                        };
                    } else {
                        return { success: true };
                    }
                } else {
                    // 非macOS平台
                    return { success: true };
                }
            } catch (error: any) {
                console.error('请求麦克风权限失败:', error);
                return { success: false, error: error.message };
            }
        });

        // 创建豆包WebSocket连接处理器
        ipcMain.handle('create-doubao-websocket', async (event, config) => {
            try {
                const WebSocket = require('ws');
                const connectId = config.connectId;
                
                console.log(`[WebSocket创建] 连接ID: ${connectId}`);
                console.log(`[WebSocket创建] URL: ${config.url}`);
                console.log(`[WebSocket创建] Headers:`, {
                    'X-Api-App-Key': config.appKey ? `${config.appKey.substring(0, 8)}...` : '(空)',
                    'X-Api-Access-Key': config.accessKey ? `${config.accessKey.substring(0, 8)}...` : '(空)',
                    'X-Api-Resource-Id': config.resourceId,
                    'X-Api-Connect-Id': connectId
                });
                
                const ws = new WebSocket(config.url, {
                    headers: {
                        'X-Api-App-Key': config.appKey,
                        'X-Api-Access-Key': config.accessKey,
                        'X-Api-Resource-Id': config.resourceId,
                        'X-Api-Connect-Id': connectId
                    }
                });

                // 存储WebSocket连接
                if (!Swordfish.doubaoWebSockets) {
                    Swordfish.doubaoWebSockets = new Map();
                }
                Swordfish.doubaoWebSockets.set(connectId, ws);

                return new Promise((resolve) => {
                    const timeout = setTimeout(() => {
                        console.log(`[WebSocket超时] 连接${connectId}超时，关闭连接`);
                        ws.close();
                        Swordfish.doubaoWebSockets?.delete(connectId);
                        resolve({ success: false, error: '连接超时 (10秒)' });
                    }, 10000);

                    ws.on('open', () => {
                        clearTimeout(timeout);
                        console.log(`豆包WebSocket连接已建立: ${connectId}`);
                        resolve({ success: true });
                    });

                    ws.on('message', (data: Buffer) => {
                        console.log(`[WebSocket消息] 连接${connectId}收到消息，长度: ${data.length}`);
                        console.log(`[WebSocket消息] 原始数据前32字节:`, Array.from(data.slice(0, 32)).map(b => b.toString(16).padStart(2, '0')).join(' '));
                        
                        // 尝试解析看是否有可读的JSON内容
                        try {
                            // 跳过前12字节的头部，尝试解析payload
                            if (data.length > 12) {
                                const payloadSize = data.readUInt32BE(8);
                                if (payloadSize > 0 && payloadSize <= data.length - 12) {
                                    const payload = data.slice(12, 12 + payloadSize);
                                    const payloadStr = payload.toString('utf8');
                                    console.log(`[WebSocket消息] Payload内容:`, payloadStr);
                                    
                                    // 尝试解析JSON
                                    try {
                                        const jsonResult = JSON.parse(payloadStr);
                                        console.log(`[WebSocket消息] 解析的JSON:`, JSON.stringify(jsonResult, null, 2));
                                    } catch (jsonError) {
                                        console.log(`[WebSocket消息] 非JSON格式的payload`);
                                    }
                                }
                            }
                        } catch (parseError) {
                            console.log(`[WebSocket消息] 解析消息内容时出错:`, parseError);
                        }
                        
                        // 转发消息到renderer进程
                        console.log(`[WebSocket消息] 正在转发消息到渲染进程: doubao-ws-message-${connectId}`);
                        event.sender.send(`doubao-ws-message-${connectId}`, data.buffer);
                        console.log(`[WebSocket消息] 消息已转发`);
                    });

                    ws.on('error', (error: any) => {
                        clearTimeout(timeout);
                        console.error(`[WebSocket错误] 连接${connectId}发生错误:`, error);
                        event.sender.send(`doubao-ws-error-${connectId}`, error.message);
                        Swordfish.doubaoWebSockets?.delete(connectId);
                        resolve({ success: false, error: 'WebSocket连接失败: ' + error.message });
                    });

                    ws.on('close', (code: number, reason: string) => {
                        clearTimeout(timeout);
                        const reasonStr = reason.toString();
                        console.log(`豆包WebSocket连接已关闭: ${connectId}, code: ${code}, reason: ${reasonStr}`);
                        
                        // 发送包含code和reason的对象，而不是错误消息字符串
                        event.sender.send(`doubao-ws-close-${connectId}`, { code, reason: reasonStr });
                        Swordfish.doubaoWebSockets?.delete(connectId);
                    });
                });
            } catch (error: any) {
                console.error('创建豆包WebSocket连接失败:', error);
                return { success: false, error: error.message };
            }
        });

        // WebSocket发送数据处理器 (使用通用处理器)
        ipcMain.on('doubao-ws-send', (event: IpcMainEvent, { connectId, data }: { connectId: string, data: ArrayBuffer }) => {
            const ws = Swordfish.doubaoWebSockets?.get(connectId);
            
            if (ws && ws.readyState === 1) { // WebSocket.OPEN = 1
                ws.send(data);
            } else {
                console.error(`WebSocket连接不可用: ${connectId}`);
            }
        });

        // WebSocket关闭处理器 (使用通用处理器)
        ipcMain.on('doubao-ws-close', (event: IpcMainEvent, connectId: string) => {
            const ws = Swordfish.doubaoWebSockets?.get(connectId);
            
            if (ws) {
                ws.close();
                Swordfish.doubaoWebSockets?.delete(connectId);
            }
        });

        // GZIP压缩/解压处理器
        ipcMain.handle('gzip-compress', async (event, data: number[]) => {
            try {
                const zlib = require('zlib');
                const buffer = Buffer.from(data);
                const compressed = zlib.gzipSync(buffer);
                return Array.from(compressed);
            } catch (error) {
                console.error('GZIP压缩失败:', error);
                return data; // 返回原数据
            }
        });

        ipcMain.handle('gzip-decompress', async (event, data: number[]) => {
            try {
                const zlib = require('zlib');
                const buffer = Buffer.from(data);
                const decompressed = zlib.gunzipSync(buffer);
                return Array.from(decompressed);
            } catch (error) {
                console.error('GZIP解压失败:', error);
                return data; // 返回原数据
            }
        });

        ipcMain.on('show-import-tmx', (event: IpcMainEvent, arg: any) => {
            Swordfish.showImportTMX(arg);
        });
        ipcMain.on('get-memory-param', (event: IpcMainEvent) => {
            Swordfish.memoryParam ? event.sender.send('set-memory', Swordfish.memoryParam) : event.preventDefault();
        });
        ipcMain.on('show-import-glossary', (event: IpcMainEvent, arg: any) => {
            Swordfish.showImportGlossary(arg);
        });
        ipcMain.on('get-glossary-param', (event: IpcMainEvent) => {
            event.sender.send('set-glossary', Swordfish.glossaryParam);
        });
        ipcMain.on('get-glossary-file', (event: IpcMainEvent) => {
            Swordfish.getGlossaryFile(event);
        });
        ipcMain.on('import-glossary-file', (event: IpcMainEvent, arg: any) => {
            Swordfish.importGlossaryFile(arg);
        });
        ipcMain.on('export-glossaries', (event: IpcMainEvent, arg: any) => {
            Swordfish.exportGlossaries(arg);
        });
        ipcMain.on('close-importGlossary', () => {
            Swordfish.importGlossaryWindow.close();
        });
        ipcMain.on('close-importTmx', () => {
            Swordfish.importTmxWindow.close();
        });
        ipcMain.on('import-tmx-file', (event: IpcMainEvent, arg: any) => {
            Swordfish.importTmxFile(arg);
        });
        ipcMain.on('remove-memories', (event: IpcMainEvent, arg: any) => {
            Swordfish.removeMemories(arg);
        });
        ipcMain.on('export-memories', (event: IpcMainEvent, arg: any) => {
            Swordfish.exportMemories(arg);
        });
        ipcMain.on('get-tmx-file', (event: IpcMainEvent) => {
            this.getTmxFile(event);
        });
        ipcMain.on('concordance-search', (event: IpcMainEvent, memories: string[]) => {
            Swordfish.showConcordanceWindow(memories);
        });
        ipcMain.on('get-concordance-memories', (event: IpcMainEvent) => {
            event.sender.send('set-concordance-memories', Swordfish.concordanceMemories);
        });
        ipcMain.on('close-concordanceSearch', () => {
            Swordfish.concordanceSearchWindow.close();
        });
        ipcMain.on('get-concordance', (event: IpcMainEvent, arg: any) => {
            Swordfish.concordanceSearch(event, arg);
        });
        ipcMain.on('get-selection', (event: IpcMainEvent) => {
            Swordfish.selectionRequest = event;
            Swordfish.mainWindow.webContents.send('get-selected-text');
        });
        ipcMain.on('selected-text', (event: IpcMainEvent, arg: { selected: string, lang?: string, srcLang: string, tgtLang: string }) => {
            Swordfish.selectionRequest.sender.send('set-selected-text', arg);
        });
        ipcMain.on('get-html-content', (event: IpcMainEvent) => {
            event.sender.send('set-content', Swordfish.htmlContent);
        });
        ipcMain.on('get-html-title', (event: IpcMainEvent) => {
            event.sender.send('set-title', Swordfish.htmlTitle);
        });
        ipcMain.on('get-html-id', (event: IpcMainEvent) => {
            event.sender.send('set-id', Swordfish.htmlId);
        });
        ipcMain.on('close-htmlViewer', (event: IpcMainEvent, id: number) => {
            BrowserWindow.fromId(id)?.close();
        });
        ipcMain.on('get-clients', (event: IpcMainEvent) => {
            this.getClients(event);
        });
        ipcMain.on('show-term-search', (event: IpcMainEvent, arg: any) => {
            Swordfish.showTermSearch(arg);
        });
        ipcMain.on('search-iate', () => {
            Swordfish.showIatePlugin();
        });
        ipcMain.on('close-iatePlugin', () => {
            Swordfish.iatePluginWindow.close();
        });
        ipcMain.on('search-termonline', () => {
            Swordfish.showTermOnlinePlugin();
        });
        ipcMain.on('close-termOnlinePlugin', () => {
            Swordfish.termOnlinePluginWindow.close();
        });
        ipcMain.on('close-termSearch', () => {
            Swordfish.termSearchWindow.close();
        });
        ipcMain.on('search-terms', (event: IpcMainEvent, arg: any) => {
            Swordfish.termSearch(arg);
        });
        
        // 术语抽取相关的IPC处理器
        ipcMain.on('show-term-extraction', (event: IpcMainEvent, arg: any) => {
            console.log('showTermExtraction收到数据:', arg);
            Swordfish.termExtractionProjectData = arg;
            console.log('保存后的termExtractionProjectData:', Swordfish.termExtractionProjectData);
            Swordfish.showTermExtraction(arg);
        });

        // 添加获取术语库的IPC处理程序
        ipcMain.on('get-term-extraction-glossaries', (event: IpcMainEvent) => {
            Swordfish.getTermExtractionGlossaries(event);
        });

        ipcMain.on('extract-terms-with-deepseek', (event: IpcMainEvent, arg: any) => {
            Swordfish.extractTermsWithDeepseek(arg);
        });

        ipcMain.on('start-term-extraction', (event: IpcMainEvent, arg: any) => {
            Swordfish.startTermExtraction(arg);
        });

        ipcMain.on('stop-term-extraction', (event: IpcMainEvent, arg: any) => {
            Swordfish.stopTermExtraction(arg);
        });

        ipcMain.on('close-term-extraction', () => {
            if (Swordfish.termExtractionWindow && !Swordfish.termExtractionWindow.isDestroyed()) {
                Swordfish.termExtractionWindow.close();
            }
        });
        ipcMain.on('add-terms-to-glossary', (event: IpcMainEvent, arg: any) => {
            Swordfish.addTermsToGlossary(arg);
        });
        ipcMain.on('get-project-names', (event: IpcMainEvent) => {
            this.getProjectNames(event);
        });
        ipcMain.on('get-subjects', (event: IpcMainEvent) => {
            this.getSubjects(event);
        });
        ipcMain.on('get-home', (event: IpcMainEvent) => {
            event.sender.send('set-home', app.getPath('home'));
        });
        ipcMain.on('get-types', (event: IpcMainEvent) => {
            this.getTypes(event);
        });
        ipcMain.on('get-charsets', (event: IpcMainEvent) => {
            this.getCharset(event);
        });
        ipcMain.on('get-version', (event: IpcMainEvent) => {
            event.sender.send('set-version', app.name + ' ' + app.getVersion());
        });
        ipcMain.on('close-preferences', () => {
            Swordfish.preferencesWindow.close();
        });
        ipcMain.on('close-defaultLangs', () => {
            Swordfish.defaultLangsWindow.close();
        });
        ipcMain.on('get-preferences', (event: IpcMainEvent) => {
            event.sender.send('set-preferences', Swordfish.currentPreferences);
        });
        ipcMain.on('preferences-set', () => {
            Swordfish.preferencesWindow.show();
            Swordfish.mainWindow.webContents.send('end-waiting');
        });
        ipcMain.on('browse-projects', (event: IpcMainEvent) => {
            this.browseProjects(event);
        });
        ipcMain.on('browse-memories', (event: IpcMainEvent) => {
            this.browseMemories(event);
        });
        ipcMain.on('browse-glossaries', (event: IpcMainEvent) => {
            this.browseGlossaries(event);
        });
        ipcMain.on('browse-srx', (event: IpcMainEvent) => {
            this.browseSRX(event);
        });
        ipcMain.on('browse-catalog', (event: IpcMainEvent) => {
            this.browseCatalog(event);
        });
        ipcMain.on('get-mt-languages', (event: IpcMainEvent) => {
            this.getMtLanguages(event);
        });
        ipcMain.on('open-license', (event: IpcMainEvent, type: string) => {
            Swordfish.openLicense(type);
        });
        ipcMain.on('get-message-param', (event: IpcMainEvent) => {
            event.sender.send('set-message', Swordfish.messageParam);
        });
        ipcMain.on('show-message', (event: IpcMainEvent, arg: any) => {
            Swordfish.showMessage(arg);
        });
        ipcMain.on('show-notification', (event: IpcMainEvent, message: string) => {
            Swordfish.showNotification(message);
        });
        ipcMain.on('add-tab', (event: IpcMainEvent, arg: Project) => {
            Swordfish.mainWindow.webContents.send('add-tab', arg);
        });
        ipcMain.on('get-segments-count', (event: IpcMainEvent, arg: any) => {
            Swordfish.getSegmenstCount(event, arg);
        });
        ipcMain.on('get-segments', (event: IpcMainEvent, arg: any) => {
            Swordfish.getSegments(event, arg);
        });
        ipcMain.on('paste-text', (event: IpcMainEvent, text: string) => {
            clipboard.writeText(text);
            Swordfish.mainWindow.webContents.paste();
        });
        ipcMain.on('save-translation', (event: IpcMainEvent, arg: any) => {
            Swordfish.saveTranslation(arg);
        });
        ipcMain.on('save-source', (event: IpcMainEvent, arg: any) => {
            Swordfish.saveSource(arg);
        });
        ipcMain.on('fix-segment-tags', (event: IpcMainEvent, arg: any) => {
            Swordfish.fixTags(arg);
        });
        ipcMain.on('open-prompt', (event: IpcMainEvent, arg: any) => {
            Swordfish.openPrompt(arg);
        });
        ipcMain.on('generate-prompt', (event: IpcMainEvent, arg: any) => {
            Swordfish.generatePrompt(arg);
        });
        ipcMain.on('paste-response', () => {
            Swordfish.insertAiResponse();
        });
        ipcMain.on('insert-response', (event: IpcMainEvent, arg: any) => {
            Swordfish.insertResponse(arg);
        });
        ipcMain.on('close-promptDialog', () => {
            if (Swordfish.promptWindow) {
                Swordfish.promptWindow.close();
            }
        });
        ipcMain.on('get-matches', (event: IpcMainEvent, arg: any) => {
            Swordfish.getMatches(arg);
        });
        ipcMain.on('get-terms', (event: IpcMainEvent, arg: any) => {
            Swordfish.getTerms(arg);
        });
        ipcMain.on('get-segment-terms', (event: IpcMainEvent, arg: any) => {
            Swordfish.getSegmentTerms(arg);
        });
        ipcMain.on('get-project-terms', (event: IpcMainEvent, arg: any) => {
            Swordfish.getProjectTerms(arg);
        });
        ipcMain.on('machine-translate', (event: IpcMainEvent, arg: any) => {
            Swordfish.machineTranslate(arg);
        });
        ipcMain.on('assemble-matches', (event: IpcMainEvent, arg: any) => {
            Swordfish.assembleMatches(arg);
        });
        ipcMain.on('assemble-matches-all', (event: IpcMainEvent, arg: any) => {
            Swordfish.assembleMatchesAll(arg);
        });
        ipcMain.on('remove-assembled-matches', (event: IpcMainEvent, arg: any) => {
            Swordfish.removeAssembledMatches(arg);
        });
        ipcMain.on('accept-match', (event: IpcMainEvent, match: Match) => {
            Swordfish.mainWindow.webContents.send('set-target', match);
        });
        ipcMain.on('fix-match', (event: IpcMainEvent, match: Match) => {
            Swordfish.fixMatch(match);
        });
        ipcMain.on('get-mt-matches', () => {
            Swordfish.mainWindow.webContents.send('get-mt-matches');
        });
        ipcMain.on('get-am-matches', () => {
            Swordfish.mainWindow.webContents.send('get-am-matches');
        });
        ipcMain.on('apply-mt-all', (event: IpcMainEvent, arg: any) => {
            Swordfish.applyMachineTranslationsAll(arg);
        });
        ipcMain.on('accept-mt-all', (event: IpcMainEvent, arg: any) => {
            Swordfish.acceptAllMachineTranslations(arg);
        });
        ipcMain.on('search-memory', () => {
            Swordfish.mainWindow.webContents.send('get-tm-matches');
        });
        ipcMain.on('search-memory-all', (event: IpcMainEvent, arg: any) => {
            Swordfish.tmTranslateAll(arg);
        });
        ipcMain.on('show-apply-tm', (event: IpcMainEvent, arg: any) => {
            Swordfish.showApplyTm(arg);
        });
        ipcMain.on('close-apply-tm', () => {
            Swordfish.applyTmWindow.close();
        });
        ipcMain.on('tm-translate', (event: IpcMainEvent, arg: any) => {
            Swordfish.tmTranslate(arg);
        });
        ipcMain.on('get-project-memories', (event: IpcMainEvent, arg: any) => {
            Swordfish.getProjectMemories(arg);
        });
        ipcMain.on('set-project-memory', (event: IpcMainEvent, arg: any) => {
            Swordfish.setProjectMemory(arg);
        });
        ipcMain.on('get-project-glossaries', (event: IpcMainEvent, arg: any) => {
            Swordfish.getProjectGlossaries(arg);
        });
        ipcMain.on('set-project-glossary', (event: IpcMainEvent, arg: any) => {
            Swordfish.setProjectGlossary(arg);
        });
        ipcMain.on('spell-language', (event: IpcMainEvent, arg: any) => {
            Swordfish.setSpellcheckerLanguage(arg);
        });
        ipcMain.on('show-spellchecker-langs', () => {
            Swordfish.showSpellCheckerLangs();
        });
        ipcMain.on('get-spellchecker-langs', (event: IpcMainEvent) => {
            Swordfish.getSpellCheckerLangs(event);
        });
        ipcMain.on('close-spellingLangs', () => {
            Swordfish.spellingLangsWindow.close();
        });
        ipcMain.on('show-sort-segments', (event: IpcMainEvent, arg: any) => {
            Swordfish.showSortSegments(arg);
        });
        ipcMain.on('get-sort-params', (event: IpcMainEvent) => {
            event.sender.send('set-params', Swordfish.sortParams);
        })
        ipcMain.on('sort-options', (event: IpcMainEvent, arg: any) => {
            Swordfish.sortOptions(arg);
        });
        ipcMain.on('show-filter-segments', (event: IpcMainEvent, arg: any) => {
            Swordfish.showFilterSegments(arg);
        });
        ipcMain.on('get-filter-params', (event: IpcMainEvent) => {
            event.sender.send('set-params', Swordfish.filterParams);
        })
        ipcMain.on('filter-options', (event: IpcMainEvent, arg: any) => {
            Swordfish.filterOptions(arg);
        });
        ipcMain.on('export-xliff-review', (event: IpcMainEvent, arg: any) => {
            Swordfish.exportXLIFF(arg);
        });
        ipcMain.on('export-xliff', (event: IpcMainEvent, arg: any) => {
            Swordfish.exportProject(arg);
        });
        ipcMain.on('export-tmx-file', (event: IpcMainEvent, arg: any) => {
            Swordfish.exportProjectTMX(arg);
        });
        ipcMain.on('export-tm-matches', (event: IpcMainEvent, arg: any) => {
            Swordfish.exportMatches(arg);
        });
        ipcMain.on('export-terms', (event: IpcMainEvent, arg: any) => {
            Swordfish.exportTerms(arg);
        });
        ipcMain.on('import-xliff-review', (event: IpcMainEvent, arg: any) => {
            Swordfish.importReviewedXLIFF();
        });
        ipcMain.on('import-xliff', () => {
            Swordfish.showImportXliff();
        });
        ipcMain.on('close-importXliff', () => {
            Swordfish.importXliffWindow.close();
        });
        ipcMain.on('browse-xliff-import', (event: IpcMainEvent) => {
            Swordfish.browseXLIFF(event);
        });
        ipcMain.on('import-xliff-file', (event: IpcMainEvent, arg: any) => {
            Swordfish.importXLIFF(arg);
        });
        ipcMain.on('files-dropped', (event: IpcMainEvent, files: string[]) => {
            Swordfish.filesDropped(files);
        });
        ipcMain.on('remove-translations', (event: IpcMainEvent, arg: any) => {
            Swordfish.removeTranslations(arg);
        });
        ipcMain.on('remove-all-matches', (event: IpcMainEvent, arg: any) => {
            Swordfish.removeMatches(arg);
        });
        ipcMain.on('remove-machine-translations', (event: IpcMainEvent, arg: any) => {
            Swordfish.removeMachineTranslations(arg);
        });
        ipcMain.on('unconfirm-translations', (event: IpcMainEvent, arg: any) => {
            Swordfish.unconfirmTranslations(arg);
        });
        ipcMain.on('pseudo-translate', (event: IpcMainEvent, arg: any) => {
            Swordfish.pseudoTranslate(arg);
        });
        ipcMain.on('copy-sources', (event: IpcMainEvent, arg: any) => {
            Swordfish.copyAllSources(arg);
        });
        ipcMain.on('confirm-translations', (event: IpcMainEvent, arg: any) => {
            Swordfish.confirmAllTranslations(arg);
        });
        ipcMain.on('accept-100-matches', (event: IpcMainEvent, arg: any) => {
            Swordfish.acceptAll100Matches(arg);
        });
        ipcMain.on('generate-statistics', (event: IpcMainEvent, arg: any) => {
            Swordfish.generateStatistics(arg);
        });
        ipcMain.on('show-tag-window', () => {
            Swordfish.showTagsWindow();
        });
        ipcMain.on('show-go-to-window', () => {
            Swordfish.showGoToWindow();
        });
        ipcMain.on('forward-tag', (event: IpcMainEvent, arg: any) => {
            Swordfish.mainWindow.webContents.send('insert-tag', arg);
        });
        ipcMain.on('show-replaceText', (event: IpcMainEvent, arg: any) => {
            Swordfish.showReplaceText(arg);
        });
        ipcMain.on('search-replace', (event: IpcMainEvent, arg: any) => {
            Swordfish.replaceText(arg);
        });
        ipcMain.on('request-apply-terminology', () => {
            Swordfish.mainWindow.webContents.send('apply-terminology');
        });
        ipcMain.on('lock-segment', (event: IpcMainEvent, arg: any) => {
            Swordfish.lockSegment(arg);
        });
        ipcMain.on('lock-duplicates', (event: IpcMainEvent, arg: any) => {
            Swordfish.lockDuplicates(arg);
        });
        ipcMain.on('unlock-all', (event: IpcMainEvent, projectId: string) => {
            Swordfish.unlockAll(projectId);
        });
        ipcMain.on('get-zoom', () => {
            Swordfish.mainWindow.webContents.send('set-zoom', { zoom: Swordfish.currentPreferences.zoomFactor });
        });
        ipcMain.on('analyze-spaces', (event: IpcMainEvent, projectId: string) => {
            Swordfish.analyzeSpaces(projectId);
        });
        ipcMain.on('analyze-tags', (event: IpcMainEvent, projectId: string) => {
            Swordfish.analyzeTags(projectId);
        });
        ipcMain.on('export-project-html', (event: IpcMainEvent, arg: any) => {
            Swordfish.exportHTML(arg);
        });
        ipcMain.on('show-change-case', () => {
            Swordfish.showChangeCase();
        });
        ipcMain.on('close-change-case', () => {
            Swordfish.changeCaseWindow.close();
        });
        ipcMain.on('change-case-to', (event: IpcMainEvent, arg: any) => {
            Swordfish.changeCaseTo(arg);
        });
        ipcMain.on('split-at', (event: IpcMainEvent, arg: any) => {
            Swordfish.splitSegment(arg);
        });
        ipcMain.on('merge-at', (event: IpcMainEvent, arg: any) => {
            Swordfish.mergeSegment(arg);
        });
        ipcMain.on('show-notes', (event: IpcMainEvent, arg: any) => {
            Swordfish.showNotes(arg);
        });
        ipcMain.on('get-initial-notes', (event: IpcMainEvent) => {
            Swordfish.notesEvent = event;
            event.sender.send('note-params', Swordfish.notesParam);
            Swordfish.getNotes(Swordfish.notesParam);
        });
        ipcMain.on('show-add-note', (event: IpcMainEvent, arg: any) => {
            Swordfish.showAddNote(arg);
        });
        ipcMain.on('close-add-note', () => {
            Swordfish.addNoteWindow.close();
        });
        ipcMain.on('add-note', (event: IpcMainEvent, arg: any) => {
            Swordfish.addNote(arg);
        });
        ipcMain.on('remove-note', (event: IpcMainEvent, arg: any) => {
            Swordfish.removeNote(arg);
        });
        ipcMain.on('get-versions', (event: IpcMainEvent) => {
            event.sender.send('set-versions', { current: app.getVersion(), latest: Swordfish.latestVersion });
        });
        ipcMain.on('close-updates', () => {
            Swordfish.updatesWindow.close();
        });
        ipcMain.on('release-history', () => {
            Swordfish.showReleaseHistory();
        });
        ipcMain.on('download-latest', () => {
            Swordfish.downloadLatest();
        });
        ipcMain.on('close-getting-started', () => {
            Swordfish.gettingStartedWindow.close();
        });
        ipcMain.on('show-help', () => {
            Swordfish.showHelp();
        });
        ipcMain.on('show-support', () => {
            Swordfish.showSupportGroup();
        });
        ipcMain.on('show-getting-started', (event: IpcMainEvent, arg: any) => {
            Swordfish.currentPreferences.showGuide = arg.showGuide;
            Swordfish.savePreferences(Swordfish.currentPreferences);
        });
        ipcMain.on('get-show guide', (event: IpcMainEvent) => {
            event.sender.send('set-show guide', { showGuide: Swordfish.currentPreferences.showGuide });
        });
        ipcMain.on('get-xmlFilters', (event: IpcMainEvent) => {
            Swordfish.getXMLFilters(event);
        });
        ipcMain.on('edit-filterConfig', (event: IpcMainEvent, arg: any) => {
            Swordfish.editXmlFilter(arg);
        });
        ipcMain.on('close-filterConfig', () => {
            Swordfish.editXmlFilterWindow.close();
        });
        ipcMain.on('get-filterData', (event: IpcMainEvent) => {
            Swordfish.getXmlFilterData(event);
        });
        ipcMain.on('add-element', (event: IpcMainEvent, arg: any) => {
            Swordfish.addElement(arg);
        });
        ipcMain.on('close-elementConfig', () => {
            Swordfish.configElementWindow.close();
        });
        ipcMain.on('get-elementConfig', (event: IpcMainEvent) => {
            Swordfish.getElementConfig(event);
        });
        ipcMain.on('save-elementConfig', (event: IpcMainEvent, arg: any) => {
            Swordfish.saveElementConfig(arg);
        });
        ipcMain.on('remove-elements', (event: IpcMainEvent, arg: any) => {
            Swordfish.removeElements(arg);
        });
        ipcMain.on('import-xmlFilter', (event: IpcMainEvent) => {
            Swordfish.importXmlFilter(event);
        });
        ipcMain.on('remove-xmlFilters', (event: IpcMainEvent, arg: any) => {
            Swordfish.removeXmlFilters(event, arg);
        });
        ipcMain.on('export-xmlFilters', (event: IpcMainEvent, arg: any) => {
            Swordfish.exportXmlFilters(arg);
        });
        ipcMain.on('show-addXmlConfiguration', (event: IpcMainEvent) => {
            Swordfish.showAddXmlConfiguration(event);
        });
        ipcMain.on('close-addXmlConfiguration', () => {
            Swordfish.addXmlConfigurationWindow.close();
        });
        ipcMain.on('add-xmlConfigurationFile', (event: IpcMainEvent, arg: any) => {
            Swordfish.addXmlConfiguration(event, arg);
        });
        ipcMain.on('close-tagsAnalysis', () => {
            Swordfish.tagsAnalysisWindow.close();
        });
        ipcMain.on('get-tagsErrors', (event: IpcMainEvent) => {
            Swordfish.getTagErrors(event);
        });
        ipcMain.on('close-spaceAnalysis', () => {
            Swordfish.spaceAnalysisWindow.close();
        });
        ipcMain.on('get-spaceErrors', (event: IpcMainEvent) => {
            Swordfish.getSpaceErrors(event);
        });
        ipcMain.on('fix-spaceErrors', (event: IpcMainEvent) => {
            Swordfish.mainWindow.webContents.send('remember-segment');
            Swordfish.fixSpaceErrors(event);
        });
        ipcMain.on('open-glossary-manager', (event: IpcMainEvent, arg: any) => {
            console.log('打开术语库管理窗口:', arg);
            Swordfish.showGlossaryManager(arg.glossaryId, arg.glossaryName);
        });
        ipcMain.on('close-glossary-manager', () => {
            if (Swordfish.glossaryManagerWindow && !Swordfish.glossaryManagerWindow.isDestroyed()) {
                Swordfish.glossaryManagerWindow.close();
            }
        });
        ipcMain.on('get-glossary-manager-terms', (event: IpcMainEvent, arg: any) => {
            console.log('[get-glossary-manager-terms] 请求参数:', JSON.stringify(arg));
            
            // 首先尝试新的 API
            Swordfish.sendRequest('/glossaries/terms', { glossary: arg.glossaryId },
                (data: any) => {
                    console.log('[get-glossary-manager-terms] 后端返回:', JSON.stringify(data));
                    if (data.terms) {
                        event.sender.send('set-glossary-manager-terms', data.terms);
                    } else {
                        event.sender.send('set-glossary-manager-terms', []);
                    }
                },
                (reason: string) => {
                    console.log('[get-glossary-manager-terms] 新API失败，尝试兼容模式:', reason);
                    
                    // 如果新 API 失败，尝试使用传统的搜索方式
                    if (reason.includes('Unknown request')) {
                        // 使用传统的术语搜索 API
                        Swordfish.sendRequest('/glossaries/search', { 
                            glossary: arg.glossaryId,
                            source: '',  // 空搜索以获取所有术语
                            target: ''
                        },
                        (searchData: any) => {
                            console.log('[get-glossary-manager-terms] 兼容模式返回:', JSON.stringify(searchData));
                            if (searchData.matches && searchData.matches.length > 0) {
                                // 转换搜索结果格式为术语格式
                                let terms = searchData.matches.map((match: any) => ({
                                    id: match.id || '',
                                    source: match.source || '',
                                    target: match.target || '',
                                    srcLang: match.srcLang || 'zh-CN',
                                    tgtLang: match.tgtLang || 'en-US',
                                    origin: match.origin || ''
                                }));
                                event.sender.send('set-glossary-manager-terms', terms);
                            } else {
                                event.sender.send('set-glossary-manager-terms', []);
                            }
                        },
                        (searchReason: string) => {
                            console.log('[get-glossary-manager-terms] 兼容模式也失败:', searchReason);
                            event.sender.send('set-glossary-manager-terms', []);
                            // 显示用户友好的错误信息
                            Swordfish.showMessage({ 
                                type: 'warning', 
                                message: `术语库功能不可用，可能需要更新到最新版本。\n错误信息: ${reason}` 
                            });
                        });
                    } else {
                        event.sender.send('set-glossary-manager-terms', []);
                    }
                }
            );
        });
        ipcMain.on('show-add-term-dialog', (event: IpcMainEvent, arg: any) => {
            Swordfish.glossaryParam = arg.glossaryId;
            Swordfish.showAddTerm(arg);
        });
        ipcMain.on('export-glossary-manager', (event: IpcMainEvent, arg: any) => {
            // 复用现有的导出功能
            Swordfish.mainWindow.webContents.send('export-glossary');
        });
        ipcMain.on('get-glossary-terms', (event: IpcMainEvent, arg: any) => {
            console.log('[get-glossary-terms] 请求参数:', JSON.stringify(arg));
            
            // 首先尝试新的 API
            Swordfish.sendRequest('/glossaries/terms', { glossary: arg.glossary },
                (data: any) => {
                    console.log('[get-glossary-terms] 后端返回完整响应:', JSON.stringify(data));
                    console.log('[全部术语] 返回:', JSON.stringify(data.terms));
                    if (data.terms) {
                        event.sender.send('set-glossary-terms', data.terms);
                    } else {
                        event.sender.send('set-glossary-terms', []);
                    }
                },
                (reason: string) => {
                    console.log('[get-glossary-terms] 新API失败，尝试兼容模式:', reason);
                    
                    // 如果新 API 失败，尝试使用传统的搜索方式
                    if (reason.includes('Unknown request')) {
                        // 使用传统的术语搜索 API
                        Swordfish.sendRequest('/glossaries/search', { 
                            glossary: arg.glossary,
                            source: '',  // 空搜索以获取所有术语
                            target: ''
                        },
                        (searchData: any) => {
                            console.log('[get-glossary-terms] 兼容模式返回:', JSON.stringify(searchData));
                            if (searchData.matches && searchData.matches.length > 0) {
                                // 转换搜索结果格式为术语格式
                                let terms = searchData.matches.map((match: any) => ({
                                    id: match.id || '',
                                    source: match.source || '',
                                    target: match.target || '',
                                    srcLang: match.srcLang || 'zh-CN',
                                    tgtLang: match.tgtLang || 'en-US',
                                    origin: match.origin || ''
                                }));
                                event.sender.send('set-glossary-terms', terms);
                            } else {
                                event.sender.send('set-glossary-terms', []);
                            }
                        },
                        (searchReason: string) => {
                            console.log('[get-glossary-terms] 兼容模式也失败:', searchReason);
                            event.sender.send('set-glossary-terms', []);
                        });
                    } else {
                        console.log('[get-glossary-terms] 请求失败:', reason);
                        event.sender.send('set-glossary-terms', []);
                    }
                }
            );
        });
        ipcMain.on('match-segment-terms', (event: IpcMainEvent, arg: any) => {
            console.log('[match-segment-terms] 请求参数:', JSON.stringify(arg));
            Swordfish.sendRequest('/glossaries/terms', { glossary: arg.glossary },
                (data: any) => {
                    console.log('[match-segment-terms] 后端返回术语数量:', data.terms ? data.terms.length : 0);
                    if (data.terms && data.terms.length > 0) {
                        // 在句段中匹配术语，传入语言方向信息支持双向匹配
                        let matchedTerms = Swordfish.matchTermsInSource(arg.source, data.terms, arg.srcLang, arg.tgtLang);
                        console.log('[match-segment-terms] 匹配到术语数量:', matchedTerms.length);
                        event.sender.send('set-matched-terms', matchedTerms);
                    } else {
                        event.sender.send('set-matched-terms', []);
                    }
                },
                (reason: string) => {
                    console.log('[match-segment-terms] 请求失败:', reason);
                    event.sender.send('set-matched-terms', []);
                }
            );
        });

        // AI 助手相关 IPC 事件
        ipcMain.on('show-ai-assistant', () => {
            Swordfish.showAIAssistant();
        });
        ipcMain.on('ai-assistant-ready', () => {
            console.log('AI助手已准备就绪');
        });
        ipcMain.on('ai-assistant-close', () => {
            if (Swordfish.aiAssistantWindow && !Swordfish.aiAssistantWindow.isDestroyed()) {
                Swordfish.aiAssistantWindow.close();
            }
        });
        ipcMain.on('ai-assistant-minimized', (event: IpcMainEvent, isMinimized: boolean) => {
            // 可以在这里处理最小化状态变化
            console.log('AI助手最小化状态:', isMinimized);
        });
        ipcMain.on('ai-assistant-toggle-maximize', () => {
            if (Swordfish.aiAssistantWindow && !Swordfish.aiAssistantWindow.isDestroyed()) {
                if (Swordfish.aiAssistantWindow.isMaximized()) {
                    Swordfish.aiAssistantWindow.unmaximize();
                } else {
                    Swordfish.aiAssistantWindow.maximize();
                }
            }
        });


        ipcMain.on('ai-chat', (event: IpcMainEvent, data: any) => {
            Swordfish.handleAIChat(event, data);
        });
        
        // AI助手窗口控制
        ipcMain.on('ai-assistant-move', (event: IpcMainEvent, data: { deltaX: number; deltaY: number }) => {
            if (Swordfish.aiAssistantWindow && !Swordfish.aiAssistantWindow.isDestroyed()) {
                const [x, y] = Swordfish.aiAssistantWindow.getPosition();
                Swordfish.aiAssistantWindow.setPosition(x + data.deltaX, y + data.deltaY);
            }
        });

        ipcMain.on('ai-assistant-maximize-toggle', () => {
            if (Swordfish.aiAssistantWindow && !Swordfish.aiAssistantWindow.isDestroyed()) {
                if (Swordfish.aiAssistantWindow.isMaximized()) {
                    Swordfish.aiAssistantWindow.unmaximize();
                } else {
                    Swordfish.aiAssistantWindow.maximize();
                }
            }
        });

        ipcMain.on('ai-assistant-minimize-toggle', (event: IpcMainEvent, isMinimized: boolean) => {
            if (Swordfish.aiAssistantWindow && !Swordfish.aiAssistantWindow.isDestroyed()) {
                if (isMinimized) {
                    // 保存当前窗口的完整边界信息
                    const bounds = Swordfish.aiAssistantWindow.getBounds();
                    Swordfish.aiAssistantOriginalBounds = bounds;
                    
                    // 获取屏幕工作区域
                    const display = screen.getPrimaryDisplay();
                    const workArea = display.workArea;
                    
                    // 最小化：移动到屏幕底部，保持宽度，设置为标题栏高度
                    const minimizedHeight = 50; // 只保留紫色标题栏的高度
                    const minimizedY = workArea.y + workArea.height - minimizedHeight;
                    
                    Swordfish.aiAssistantWindow.setBounds({
                        x: bounds.x,  // 保持水平位置
                        y: minimizedY,  // 移动到底部
                        width: bounds.width,  // 保持宽度
                        height: minimizedHeight  // 标题栏高度
                    });
                } else {
                    // 恢复到原始位置和尺寸
                    if (Swordfish.aiAssistantOriginalBounds) {
                        Swordfish.aiAssistantWindow.setBounds(Swordfish.aiAssistantOriginalBounds);
                    } else {
                        // 如果没有保存的边界，恢复到默认尺寸和位置
                        const bounds = Swordfish.aiAssistantWindow.getBounds();
                        Swordfish.aiAssistantWindow.setBounds({
                            x: bounds.x,
                            y: Math.max(50, bounds.y - 400), // 向上移动
                            width: bounds.width,
                            height: 500
                        });
                    }
                }
            }
            console.log('AI助手最小化状态:', isMinimized);
        });
        
        ipcMain.on('get-selected-text', (event: IpcMainEvent) => {
            // 获取主窗口中选中的文本
            Swordfish.mainWindow.webContents.send('get-selected-text-for-ai');
        });
        ipcMain.on('get-translation-context', (event: IpcMainEvent) => {
            // 获取当前翻译上下文
            Swordfish.mainWindow.webContents.send('get-translation-context-for-ai');
        });

        // AI设置相关IPC处理
        ipcMain.on('ai-assistant-open-settings', () => {
            Swordfish.openAISettings();
        });

        ipcMain.on('test-anythingllm-connection', (event: IpcMainEvent, config: { url: string; apiKey: string }) => {
            Swordfish.testAnythingLLMConnection(event, config);
        });

        ipcMain.on('get-anythingllm-workspaces', (event: IpcMainEvent, config: { url: string; apiKey: string }) => {
            Swordfish.getAnythingLLMWorkspaces(event, config);
        });

        ipcMain.on('get-current-ai-settings', (event: IpcMainEvent) => {
            const settings = Swordfish.getAISettings();
            event.reply('current-ai-settings', settings);
        });

        ipcMain.on('save-ai-settings', (event: IpcMainEvent, settings: any) => {
            const success = Swordfish.saveAISettings(settings);
            event.reply('ai-settings-saved', success);
        });

        ipcMain.on('close-ai-settings', () => {
            if (Swordfish.aiSettingsWindow && !Swordfish.aiSettingsWindow.isDestroyed()) {
                Swordfish.aiSettingsWindow.close();
            }
        });

        ipcMain.on('minimize-ai-settings', () => {
            if (Swordfish.aiSettingsWindow && !Swordfish.aiSettingsWindow.isDestroyed()) {
                Swordfish.aiSettingsWindow.minimize();
            }
        });

        ipcMain.on('toggle-maximize-ai-settings', () => {
            if (Swordfish.aiSettingsWindow && !Swordfish.aiSettingsWindow.isDestroyed()) {
                if (Swordfish.aiSettingsWindow.isMaximized()) {
                    Swordfish.aiSettingsWindow.unmaximize();
                } else {
                    Swordfish.aiSettingsWindow.maximize();
                }
            }
        });
    } // end constructor

    static deleteAllTags(background: string, foreground: string): void {
        let tagsFolder: string = Swordfish.path.join(app.getPath('userData'), 'images');
        if (existsSync(tagsFolder)) {
            rmSync(tagsFolder, { recursive: true, force: true });
        }
        mkdirSync(tagsFolder);
        let colors: any = { background: background, foreground: foreground };
        writeFileSync(Swordfish.path.join(app.getPath('userData'), 'images', 'tagColors.json'), JSON.stringify(colors, null, 2));
        if (app.isReady()) {
            Swordfish.mainWindow.webContents.send('tags-deleted');
        }
    }

    static createWindow(): void {
        if (Swordfish.currentDefaults === undefined) {
            let size: Size = screen.getPrimaryDisplay().workAreaSize;
            Swordfish.currentDefaults = { width: Math.round(size.width * 0.95), height: Math.round(size.height * 0.95), x: 0, y: 0 };
        }
        this.mainWindow = new BrowserWindow({
            title: app.name,
            width: this.currentDefaults.width,
            height: this.currentDefaults.height,
            x: this.currentDefaults.x,
            y: this.currentDefaults.y,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            },
            show: false,
            icon: this.iconPath
        });
        this.mainWindow.webContents.on('context-menu', (event: Electron.Event, params: any) => {
            const menu = new Menu();
            // Add each spelling suggestion
            for (const suggestion of params.dictionarySuggestions) {
                menu.append(new MenuItem({
                    label: suggestion,
                    click: () => { this.mainWindow.webContents.replaceMisspelling(suggestion); }
                }));
            }
            // Allow users to add the misspelled word to the dictionary
            if (params.misspelledWord) {
                menu.append(new MenuItem({ type: 'separator' }));
                menu.append(
                    new MenuItem({
                        label: 'Add to dictionary',
                        click: () => { this.mainWindow.webContents.session.addWordToSpellCheckerDictionary(params.misspelledWord); }
                    })
                );
            }
            menu.popup();
        });
        let fileMenu: Menu = Menu.buildFromTemplate([
            { label: '打开...', accelerator: 'CmdOrCtrl+O', click: () => { Swordfish.addFile(); } }
        ]);
        let tagsMenu: Menu = Menu.buildFromTemplate([
            { label: '插入标签"1"', accelerator: 'CmdOrCtrl+1', click: () => { Swordfish.mainWindow.webContents.send('insert-tag', { tag: 1 }); } },
            { label: '插入标签"2"', accelerator: 'CmdOrCtrl+2', click: () => { Swordfish.mainWindow.webContents.send('insert-tag', { tag: 2 }); } },
            { label: '插入标签"3"', accelerator: 'CmdOrCtrl+3', click: () => { Swordfish.mainWindow.webContents.send('insert-tag', { tag: 3 }); } },
            { label: '插入标签"4"', accelerator: 'CmdOrCtrl+4', click: () => { Swordfish.mainWindow.webContents.send('insert-tag', { tag: 4 }); } },
            { label: '插入标签"5"', accelerator: 'CmdOrCtrl+5', click: () => { Swordfish.mainWindow.webContents.send('insert-tag', { tag: 5 }); } },
            { label: '插入标签"6"', accelerator: 'CmdOrCtrl+6', click: () => { Swordfish.mainWindow.webContents.send('insert-tag', { tag: 6 }); } },
            { label: '插入标签"7"', accelerator: 'CmdOrCtrl+7', click: () => { Swordfish.mainWindow.webContents.send('insert-tag', { tag: 7 }); } },
            { label: '插入标签"8"', accelerator: 'CmdOrCtrl+8', click: () => { Swordfish.mainWindow.webContents.send('insert-tag', { tag: 8 }); } },
            { label: '插入标签"9"', accelerator: 'CmdOrCtrl+9', click: () => { Swordfish.mainWindow.webContents.send('insert-tag', { tag: 9 }); } },
            { label: '插入标签"10"', accelerator: 'CmdOrCtrl+0', click: () => { Swordfish.mainWindow.webContents.send('insert-tag', { tag: 10 }); } }
        ]);
        let editMenu: Menu = Menu.buildFromTemplate([
            { label: '撤销', accelerator: 'CmdOrCtrl+Z', click: () => { Swordfish.undo() } },
            new MenuItem({ type: 'separator' }),
            { label: '剪切', accelerator: 'CmdOrCtrl+X', click: () => { Swordfish.cut() } },
            { label: '复制', accelerator: 'CmdOrCtrl+C', click: () => { Swordfish.copy(); } },
            { label: '粘贴', accelerator: 'CmdOrCtrl+V', click: () => { Swordfish.paste() } },
            { label: '全选', accelerator: 'CmdOrCtrl+A', click: () => { Swordfish.selectAll(); } },
            new MenuItem({ type: 'separator' }),
            { label: '编辑上一片段', accelerator: 'PageUp', click: () => { Swordfish.mainWindow.webContents.send('previous-segment'); } },
            { label: '编辑下一片段', accelerator: 'PageDown', click: () => { Swordfish.mainWindow.webContents.send('next-segment'); } },
            { label: '跳转到片段...', accelerator: 'CmdOrCtrl+G', click: () => { Swordfish.mainWindow.webContents.send('go-to'); } },
            new MenuItem({ type: 'separator' }),
            { label: '编辑源文本', accelerator: 'Alt+F2', click: () => { Swordfish.mainWindow.webContents.send('edit-source'); } },
            new MenuItem({ type: 'separator' }),
            { label: '编辑下一个未翻译片段', accelerator: 'CmdOrCtrl+U', click: () => { Swordfish.mainWindow.webContents.send('next-untranslated'); } },
            { label: '编辑下一个未确认片段', accelerator: 'CmdOrCtrl+Shift+U', click: () => { Swordfish.mainWindow.webContents.send('next-unconfirmed'); } },
            new MenuItem({ type: 'separator' }),
            { label: '保存片段更改', accelerator: 'Alt+Enter', click: () => { Swordfish.mainWindow.webContents.send('save-edit', { confirm: false, next: 'none' }); } },
            { label: '放弃片段更改', accelerator: 'Esc', click: () => { Swordfish.mainWindow.webContents.send('cancel-edit'); } },
            new MenuItem({ type: 'separator' }),
            { label: '大小写转换', accelerator: 'CmdOrCtrl+Alt+C', click: () => { Swordfish.mainWindow.webContents.send('change-case'); } },
            new MenuItem({ type: 'separator' }),
            { label: '拆分片段', accelerator: 'CmdOrCtrl+H', click: () => { Swordfish.mainWindow.webContents.send('split-segment'); } },
            { label: '合并到下一个片段', accelerator: 'CmdOrCtrl+J', click: () => { Swordfish.mainWindow.webContents.send('merge-next'); } },
            new MenuItem({ type: 'separator' }),
            { label: '替换文本', accelerator: 'CmdOrCtrl+Alt+F', click: () => { Swordfish.mainWindow.webContents.send('replace-text'); } },
            new MenuItem({ type: 'separator' }),
            { label: '插入标签', accelerator: 'CmdOrCtrl+T', click: () => { Swordfish.mainWindow.webContents.send('insert-tag', {}); } },
            new MenuItem({ label: '插入标签...', submenu: tagsMenu }),
            { label: '插入下一个标签', accelerator: 'CmdOrCtrl+Shift+T', click: () => { Swordfish.mainWindow.webContents.send('insert-next-tag'); } },
            { label: '插入剩余标签', accelerator: 'CmdOrCtrl+Alt+T', click: () => { Swordfish.mainWindow.webContents.send('insert-remaining-tags'); } },
            { label: '移除所有标签', accelerator: 'CmdOrCtrl+Alt+Shift+R', click: () => { Swordfish.mainWindow.webContents.send('remove-tags'); } },
        ]);
        let nextMT: string = 'Alt+Right';
        let previousMT: string = 'Alt+Left';
        if (process.platform === 'darwin') {
            nextMT = 'Ctrl+Alt+Right';
            previousMT = 'Ctrl+Alt+Left';
        }
        let viewMenu: Menu = Menu.buildFromTemplate([
            { label: '项目', accelerator: 'F6', click: () => { Swordfish.viewProjects(); } },
            { label: '记忆库', accelerator: 'F7', click: () => { Swordfish.viewMemories(); } },
            { label: '术语库', accelerator: 'F8', click: () => { Swordfish.viewGlossaries(); } },
            new MenuItem({ type: 'separator' }),
            { label: '片段排序', accelerator: 'F3', click: () => { Swordfish.mainWindow.webContents.send('sort-segments'); } },
            { label: '片段过滤', accelerator: 'CmdOrCtrl+F', click: () => { Swordfish.mainWindow.webContents.send('filter-segments'); } },
            new MenuItem({ type: 'separator' }),
            { label: '显示/隐藏批注', accelerator: 'F2', click: () => { Swordfish.toggleNotes(); } },
            new MenuItem({ type: 'separator' }),
            { label: '关闭选中标签页', accelerator: 'CmdOrCtrl+W', click: () => { Swordfish.closeSelectedTab(); } },
            new MenuItem({ type: 'separator' }),
            { label: '首页', accelerator: 'CmdOrCtrl+Shift+PageUp', click: () => { Swordfish.mainWindow.webContents.send('first-page'); } },
            { label: '上一页', accelerator: 'CmdOrCtrl+PageUp', click: () => { Swordfish.mainWindow.webContents.send('previous-page'); } },
            { label: '下一页', accelerator: 'CmdOrCtrl+PageDown', click: () => { Swordfish.mainWindow.webContents.send('next-page'); } },
            { label: '末页', accelerator: 'CmdOrCtrl+Shift+PageDown', click: () => { Swordfish.mainWindow.webContents.send('last-page'); } },
            new MenuItem({ type: 'separator' }),
            { label: '下一个记忆库匹配', accelerator: 'CmdOrCtrl+Alt+Right', click: () => { Swordfish.mainWindow.webContents.send('next-match'); } },
            { label: '上一个记忆库匹配', accelerator: 'CmdOrCtrl+Alt+Left', click: () => { Swordfish.mainWindow.webContents.send('previous-match'); } },
            new MenuItem({ type: 'separator' }),
            { label: '下一个机器翻译', accelerator: nextMT, click: () => { Swordfish.mainWindow.webContents.send('next-mt'); } },
            { label: '上一个机器翻译', accelerator: previousMT, click: () => { Swordfish.mainWindow.webContents.send('previous-mt'); } },
            new MenuItem({ type: 'separator' }),
            new MenuItem({ label: '切换全屏', role: 'togglefullscreen' })
        ]);
        if (!app.isPackaged) {
            viewMenu.append(new MenuItem({ label: '打开开发者工具', accelerator: 'F12', click: () => { BrowserWindow.getFocusedWindow()?.webContents.openDevTools() } }));
        }
        let projectsMenu: Menu = Menu.buildFromTemplate([
            { label: '新建项目', accelerator: 'CmdOrCtrl+N', click: () => { Swordfish.showAddProject(); } },
            { label: '编辑项目', click: () => { Swordfish.editProject(); } },
            { label: '翻译项目', click: () => { Swordfish.translateProjects(); } },
            { label: '导出翻译', accelerator: 'CmdOrCtrl+Alt+S', click: () => { Swordfish.mainWindow.webContents.send('export-translations'); } },
            { label: '导出翻译为TMX文件', click: () => { Swordfish.mainWindow.webContents.send('export-translations-tmx'); } },
            new MenuItem({ type: 'separator' }),
            { label: '导出为XLIFF 2.0', click: () => { Swordfish.mainWindow.webContents.send('export-xliff-review'); } },
            { label: '从XLIFF文件更新', click: () => { Swordfish.importReviewedXLIFF() } },
            new MenuItem({ type: 'separator' }),
            { label: '导出所有记忆库匹配为TMX', click: () => { Swordfish.mainWindow.webContents.send('export-matches'); } },
            { label: '导出所有已识别术语为TBX', click: () => { Swordfish.mainWindow.webContents.send('export-terminology-all'); } },
            new MenuItem({ type: 'separator' }),
            { label: '移除项目', click: () => { Swordfish.mainWindow.webContents.send('remove-projects'); } },
            new MenuItem({ type: 'separator' }),
            { label: '项目统计', click: () => { Swordfish.mainWindow.webContents.send('request-statistics'); } },
            { label: '导出HTML', accelerator: 'F5', click: () => { Swordfish.mainWindow.webContents.send('export-html'); } },
            new MenuItem({ type: 'separator' }),
            { label: '导入项目', click: () => { Swordfish.showImportXliff(); } },
            { label: '导出项目', click: () => { Swordfish.mainWindow.webContents.send('export-project'); } }
        ]);
        let memoriesMenu: Menu = Menu.buildFromTemplate([
            { label: '添加记忆库', click: () => { Swordfish.showAddMemory(); } },
            { label: '移除记忆库', click: () => { Swordfish.removeMemory(); } },
            new MenuItem({ type: 'separator' }),
            { label: '添加远程TM记忆库', click: () => { Swordfish.showServerSettings('memory'); } },
            new MenuItem({ type: 'separator' }),
            { label: '进行术语库搜索', accelerator: 'CmdOrCtrl+Y', click: () => { Swordfish.mainWindow.webContents.send('concordance-requested'); } },
            new MenuItem({ type: 'separator' }),
            { label: '导入TMX文件', click: () => { Swordfish.mainWindow.webContents.send('import-tmx'); } },
            { label: '导出记忆库为TMX文件', click: () => { Swordfish.mainWindow.webContents.send('export-tmx'); } }
        ]);
        let glossariesMenu: Menu = Menu.buildFromTemplate([
            { label: '添加术语库', click: () => { Swordfish.showAddGlossary(); } },
            { label: '移除术语库', click: () => { Swordfish.removeGlossary(); } },
            new MenuItem({ type: 'separator' }),
            { label: '添加远程TM术语库', click: () => { Swordfish.showServerSettings('glossary'); } },
            new MenuItem({ type: 'separator' }),
            { label: '在术语库中搜索术语', accelerator: 'CmdOrCtrl+D', click: () => { Swordfish.mainWindow.webContents.send('term-search-requested'); } },
            { label: '添加术语到术语库', accelerator: 'CmdOrCtrl+B', click: () => { Swordfish.mainWindow.webContents.send('add-term-requested'); } },
            new MenuItem({ type: 'separator' }),
            { label: '在IATE上搜索', accelerator: 'CmdOrCtrl+Alt+I', click: () => { Swordfish.showIatePlugin(); } },
            { label: '在术语在线上搜索', accelerator: 'CmdOrCtrl+Alt+T', click: () => { Swordfish.showTermOnlinePlugin(); } },
            new MenuItem({ type: 'separator' }),
            { label: '导入术语库', click: () => { Swordfish.mainWindow.webContents.send('import-glossary'); } },
            { label: '导出术语库', click: () => { Swordfish.mainWindow.webContents.send('export-glossary'); } }
        ]);
        let aiMenu: Menu = Menu.buildFromTemplate([
            { label: '使用AI修复标签', accelerator: 'CmdOrCtrl+Shift+Alt+T', click: () => { Swordfish.mainWindow.webContents.send('fix-tags'); } },
            { label: '使用AI修复TM匹配', accelerator: 'CmdOrCtrl+Shift+M', click: () => { Swordfish.mainWindow.webContents.send('fix-selected-match'); } },
            new MenuItem({ type: 'separator' }),
            { label: '打开AI提示对话框', accelerator: 'CmdOrCtrl+Shift+P', click: () => { Swordfish.mainWindow.webContents.send('open-ai-prompt'); } },
            { label: '将AI提示复制到剪贴板', accelerator: 'CmdOrCtrl+Shift+C', click: () => { Swordfish.mainWindow.webContents.send('copy-ai-prompt'); } },
            new MenuItem({ type: 'separator' }),
            { label: '在片段中插入AI响应', accelerator: 'CmdOrCtrl+Shift+R', click: () => { Swordfish.insertAiResponse(); } }
        ]);
        let helpMenu: Menu = Menu.buildFromTemplate([
            { label: 'Swordfish用户指南', accelerator: 'F1', click: () => { this.showHelp(); } },
            { label: '入门指南', click: () => { Swordfish.showGettingStarted(); } },
            new MenuItem({ type: 'separator' }),
            { label: '检查更新...', click: () => { this.checkUpdates(false); } },
            { label: '查看许可证', click: () => { this.showLicenses({ from: 'menu' }); } },
            new MenuItem({ type: 'separator' }),
            { label: '发布历史', click: () => { Swordfish.showReleaseHistory(); } },
            { label: '支持小组', click: () => { this.showSupportGroup(); } }
        ]);
        let nextUntranslatedKey = 'Alt+Down';
        let nextUnconfirmedKey = 'Alt+Shift+Down';
        if (process.platform === 'darwin') {
            nextUntranslatedKey = 'Ctrl+Alt+Down';
            nextUnconfirmedKey = 'Ctrl+Shift+Down';
        }
        let termsMenu: Menu = Menu.buildFromTemplate([
            { label: '插入术语"1"', accelerator: 'CmdOrCtrl+Alt+1', click: () => { Swordfish.mainWindow.webContents.send('insert-tem', { term: 1 }); } },
            { label: '插入术语"2"', accelerator: 'CmdOrCtrl+Alt+2', click: () => { Swordfish.mainWindow.webContents.send('insert-tem', { term: 2 }); } },
            { label: '插入术语"3"', accelerator: 'CmdOrCtrl+Alt+3', click: () => { Swordfish.mainWindow.webContents.send('insert-tem', { term: 3 }); } },
            { label: '插入术语"4"', accelerator: 'CmdOrCtrl+Alt+4', click: () => { Swordfish.mainWindow.webContents.send('insert-tem', { term: 4 }); } },
            { label: '插入术语"5"', accelerator: 'CmdOrCtrl+Alt+5', click: () => { Swordfish.mainWindow.webContents.send('insert-tem', { term: 5 }); } },
            { label: '插入术语"6"', accelerator: 'CmdOrCtrl+Alt+6', click: () => { Swordfish.mainWindow.webContents.send('insert-tem', { term: 6 }); } },
            { label: '插入术语"7"', accelerator: 'CmdOrCtrl+Alt+7', click: () => { Swordfish.mainWindow.webContents.send('insert-tem', { term: 7 }); } },
            { label: '插入术语"8"', accelerator: 'CmdOrCtrl+Alt+8', click: () => { Swordfish.mainWindow.webContents.send('insert-tem', { term: 8 }); } },
            { label: '插入术语"9"', accelerator: 'CmdOrCtrl+Alt+9', click: () => { Swordfish.mainWindow.webContents.send('insert-tem', { term: 9 }); } },
            { label: '插入术语"10"', accelerator: 'CmdOrCtrl+Alt+0', click: () => { Swordfish.mainWindow.webContents.send('insert-tem', { term: 10 }); } }
        ]);
        let tasksMenu: Menu = Menu.buildFromTemplate([
            { label: '确认翻译', accelerator: 'CmdOrCtrl+E', click: () => { Swordfish.mainWindow.webContents.send('save-edit', { confirm: true, next: 'none' }); } },
            { label: '取消确认翻译', accelerator: 'CmdOrCtrl+Shift+E', click: () => { Swordfish.mainWindow.webContents.send('save-edit', { confirm: false, next: 'none', unconfirm: true }); } },
            { label: '确认并跳转到下一个未翻译', accelerator: nextUntranslatedKey, click: () => { Swordfish.mainWindow.webContents.send('save-edit', { confirm: true, next: 'untranslated' }); } },
            { label: '确认并跳转到下一个未确认', accelerator: nextUnconfirmedKey, click: () => { Swordfish.mainWindow.webContents.send('save-edit', { confirm: true, next: 'unconfirmed' }); } },
            new MenuItem({ type: 'separator' }),
            { label: '确认所有翻译', click: () => { Swordfish.mainWindow.webContents.send('confirm-all'); } },
            { label: '取消确认所有翻译', click: () => { Swordfish.mainWindow.webContents.send('unconfirm-all'); } },
            { label: '移除所有翻译', click: () => { Swordfish.mainWindow.webContents.send('remove-all'); } },
            new MenuItem({ type: 'separator' }),
            { label: '锁定/解锁片段', accelerator: 'F4', click: () => { Swordfish.mainWindow.webContents.send('toggle-lock'); } },
            { label: '锁定重复片段', click: () => { Swordfish.mainWindow.webContents.send('lock-repeated'); } },
            { label: '解锁所有片段', click: () => { Swordfish.mainWindow.webContents.send('unlock-segments'); } },
            new MenuItem({ type: 'separator' }),
            { label: '复制源文本到目标文本', accelerator: 'CmdOrCtrl+P', click: () => { Swordfish.mainWindow.webContents.send('copy-source'); } },
            { label: '将源文本复制到所有空目标文本', accelerator: 'CmdOrCtrl+Shift+P', click: () => { Swordfish.mainWindow.webContents.send('copy-all-sources'); } },
            { label: '伪翻译未翻译片段', click: () => { Swordfish.mainWindow.webContents.send('pseudo-translate'); } },
            new MenuItem({ type: 'separator' }),
            { label: '从记忆库获取翻译', accelerator: 'CmdOrCtrl+M', click: () => { Swordfish.mainWindow.webContents.send('get-tm-matches'); } },
            { label: '接受记忆库翻译匹配', accelerator: 'CmdOrCtrl+Alt+M', click: () => { Swordfish.mainWindow.webContents.send('accept-tm-match'); } },
            { label: '将记忆库翻译应用到所有片段', click: () => { Swordfish.mainWindow.webContents.send('apply-tm-all'); } },
            { label: '接受所有100%匹配', click: () => { Swordfish.mainWindow.webContents.send('accept-all-matches'); } },
            { label: '移除所有记忆库翻译匹配', click: () => { Swordfish.mainWindow.webContents.send('remove-matches'); } },
            new MenuItem({ type: 'separator' }),
            { label: '获取机器翻译', accelerator: 'CmdOrCtrl+L', click: () => { Swordfish.mainWindow.webContents.send('get-mt-matches'); } },
            { label: '接受机器翻译', accelerator: 'CmdOrCtrl+Alt+L', click: () => { Swordfish.mainWindow.webContents.send('accept-mt-match'); } },
            { label: '将机器翻译应用到所有片段', click: () => { Swordfish.mainWindow.webContents.send('apply-mt-all'); } },
            { label: '接受所有机器翻译', click: () => { Swordfish.mainWindow.webContents.send('accept-all-mt'); } },
            { label: '移除所有机器翻译', click: () => { Swordfish.mainWindow.webContents.send('remove-mt-all'); } },
            new MenuItem({ type: 'separator' }),
            { label: '获取自动翻译', accelerator: 'CmdOrCtrl+R', click: () => { Swordfish.mainWindow.webContents.send('get-am-matches'); } },
            { label: '将自动翻译应用到所有片段', click: () => { Swordfish.mainWindow.webContents.send('apply-am-all'); } },
            { label: '移除所有自动翻译', click: () => { Swordfish.mainWindow.webContents.send('remove-am-all'); } },
            new MenuItem({ type: 'separator' }),
            { label: '获取术语库术语', accelerator: 'CmdOrCtrl+K', click: () => { Swordfish.mainWindow.webContents.send('apply-terminology'); } },
            { label: '插入选定术语', accelerator: 'CmdOrCtrl+Alt+K', click: () => { Swordfish.mainWindow.webContents.send('insert-tem', { selected: true }); } },
            { label: '选择上一个术语', accelerator: 'CmdOrCtrl+Alt+Up', click: () => { Swordfish.mainWindow.webContents.send('select-previous-term'); } },
            { label: '选择下一个术语', accelerator: 'CmdOrCtrl+Alt+Down', click: () => { Swordfish.mainWindow.webContents.send('select-next-term'); } },
            new MenuItem({ label: '插入术语...', submenu: termsMenu }),
            { label: '获取所有片段的术语', click: () => { Swordfish.mainWindow.webContents.send('apply-terminology-all'); } }
        ]);
        let qaMenu: Menu = Menu.buildFromTemplate([
            { label: '检查内联标签', accelerator: 'F9', click: () => { Swordfish.mainWindow.webContents.send('tags-analysis'); } },
            { label: '检查初始/尾随空格', accelerator: 'F10', click: () => { Swordfish.mainWindow.webContents.send('spaces-analysis'); } }
        ]);
        let template: MenuItem[] = [
            new MenuItem({ label: '文件', role: 'fileMenu', submenu: fileMenu }),
            new MenuItem({ label: '编辑', role: 'editMenu', submenu: editMenu }),
            new MenuItem({ label: '视图', role: 'viewMenu', submenu: viewMenu }),
            new MenuItem({ label: '项目', submenu: projectsMenu }),
            new MenuItem({ label: '记忆库', submenu: memoriesMenu }),
            new MenuItem({ label: '术语库', submenu: glossariesMenu }),
            new MenuItem({ label: '任务', submenu: tasksMenu }),
            new MenuItem({ label: '质量检查', submenu: qaMenu }),
            new MenuItem({ label: 'AI', submenu: aiMenu }),
            new MenuItem({ label: '帮助', role: 'help', submenu: helpMenu })
        ];
        if (process.platform === 'darwin') {
            let appleMenu: Menu = Menu.buildFromTemplate([
                new MenuItem({ label: '关于...', click: () => { this.showAbout(); } }),
                new MenuItem({
                    label: '偏好设置...', submenu: [
                        { label: '设置', accelerator: 'Cmd+,', click: () => { this.showPreferences(); } }
                    ]
                }),
                new MenuItem({ type: 'separator' }),
                new MenuItem({
                    label: '服务', role: 'services', submenu: [
                        { label: '无服务适用', enabled: false }
                    ]
                }),
                new MenuItem({ type: 'separator' }),
                new MenuItem({ label: '退出Swordfish', accelerator: 'Cmd+Q', role: 'quit', click: () => { app.quit(); } })
            ]);
            template.unshift(new MenuItem({ label: 'Swordfish', role: 'appMenu', submenu: appleMenu }));
        } else {
            let help: MenuItem = template.pop() as MenuItem;
            template.push(new MenuItem({
                label: '设置', submenu: [
                    { label: '偏好设置', click: () => { this.showPreferences(); } }
                ]
            }));
            template.push(help);
        }
        if (process.platform === 'win32') {
            fileMenu.append(new MenuItem({ type: 'separator' }));
            fileMenu.append(new MenuItem({ label: '退出', accelerator: 'Alt+F4', role: 'quit', click: () => { app.quit(); } }));
            helpMenu.append(new MenuItem({ type: 'separator' }));
            helpMenu.append(new MenuItem({ label: '关于...', click: () => { this.showAbout(); } }));
        }
        if (process.platform === 'linux') {
            fileMenu.append(new MenuItem({ type: 'separator' }));
            fileMenu.append(new MenuItem({ label: '退出', accelerator: 'Ctrl+Q', role: 'quit', click: () => { app.quit(); } }));
            helpMenu.append(new MenuItem({ type: 'separator' }));
            helpMenu.append(new MenuItem({ label: '关于...', click: () => { this.showAbout(); } }));
        }
        Menu.setApplicationMenu(Menu.buildFromTemplate(template));
    }

    static undo() {
        let focusedWindow = BrowserWindow.getFocusedWindow();
        if (focusedWindow) {
            focusedWindow.webContents.undo();
        }
    }

    static cut() {
        let focusedWindow = BrowserWindow.getFocusedWindow();
        if (focusedWindow) {
            focusedWindow.webContents.cut();
        }
    }

    static copy() {
        let focusedWindow = BrowserWindow.getFocusedWindow();
        if (focusedWindow) {
            focusedWindow.webContents.copy();
        }
    }

    static paste() {
        let focusedWindow = BrowserWindow.getFocusedWindow();
        if (focusedWindow) {
            focusedWindow.webContents.paste();
        }
    }

    static selectAll() {
        let focusedWindow = BrowserWindow.getFocusedWindow();
        if (focusedWindow) {
            focusedWindow.webContents.selectAll();
        }
    }

    stopServer(): void {
        let instance: Swordfish = this;
        Swordfish.sendRequest('/', { command: 'stop' },
            (data: any) => {
                if (data.status === 'OK') {
                    instance.ls.kill();
                    app.quit();
                    } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    loadDefaults(): void {
        let defaultsFile: string = Swordfish.path.join(app.getPath('appData'), app.name, 'defaults.json');
        if (existsSync(defaultsFile)) {
            try {
                let data: Buffer = readFileSync(defaultsFile);
                Swordfish.currentDefaults = JSON.parse(data.toString());
                } catch (err) {
                console.error(err);
            }
        }
    }

    saveDefaults(): void {
        let defaultsFile: string = Swordfish.path.join(app.getPath('appData'), app.name, 'defaults.json');
        writeFileSync(defaultsFile, JSON.stringify(Swordfish.mainWindow.getBounds(), null, 2));
    }

    static setHeight(arg: { window: string, width: number, height: number }) {
        if ('about' === arg.window) {
            Swordfish.aboutWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('serverSettings' === arg.window) {
            Swordfish.serverSettingsWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('browseDatabases' === arg.window) {
            Swordfish.browseDatabasesWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('licenses' === arg.window) {
            Swordfish.licensesWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('addProject' === arg.window) {
            Swordfish.addProjectWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('editProject' === arg.window) {
            Swordfish.editProjectWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('addFile' === arg.window) {
            Swordfish.addFileWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('tags' === arg.window) {
            Swordfish.tagsWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('goTo' === arg.window) {
            Swordfish.goToWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('replaceText' === arg.window) {
            Swordfish.replaceTextWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('systemInfo' === arg.window) {
            Swordfish.systemInfoWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('addMemory' === arg.window) {
            Swordfish.addMemoryWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('addGlossary' === arg.window) {
            Swordfish.addGlossaryWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('addTerm' === arg.window) {
            Swordfish.addTermWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('importGlossary' === arg.window) {
            Swordfish.importGlossaryWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('importTmx' === arg.window) {
            Swordfish.importTmxWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('concordanceSearch' === arg.window) {
            Swordfish.concordanceSearchWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('termSearch' === arg.window) {
            Swordfish.termSearchWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('preferences' === arg.window) {
            Swordfish.preferencesWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('defaultLangs' === arg.window) {
            Swordfish.defaultLangsWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('applyTm' === arg.window) {
            Swordfish.applyTmWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('spellingLangs' === arg.window) {
            Swordfish.spellingLangsWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('sortSegments' === arg.window) {
            Swordfish.sortSegmentsWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('filterSegments' === arg.window) {
            Swordfish.filterSegmentsWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('importXliff' === arg.window) {
            Swordfish.importXliffWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('changeCase' === arg.window) {
            Swordfish.changeCaseWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('notes' === arg.window) {
            Swordfish.notesWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('addNote' === arg.window) {
            Swordfish.addNoteWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('updates' === arg.window) {
            Swordfish.updatesWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('gettingStarted' === arg.window) {
            Swordfish.gettingStartedWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('editXmlFilter' === arg.window) {
            Swordfish.editXmlFilterWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('configElement' === arg.window) {
            Swordfish.configElementWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('addXmlConfiguration' === arg.window) {
            Swordfish.addXmlConfigurationWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('tagsAnalysis' === arg.window) {
            Swordfish.tagsAnalysisWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('spaceAnalysis' === arg.window) {
            Swordfish.spaceAnalysisWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('promptDialog' === arg.window) {
            Swordfish.promptWindow.setContentSize(arg.width, arg.height, true);
        }
        if ('iatePlugin' === arg.window) {
            Swordfish.iatePluginWindow.setContentSize(arg.width, arg.height, true);
        }
    }

    static loadPreferences(): void {
        let dark: string = 'file://' + Swordfish.path.join(app.getAppPath(), 'css', 'dark.css');
        let light: string = 'file://' + Swordfish.path.join(app.getAppPath(), 'css', 'light.css');
        let highContrast: string = 'file://' + Swordfish.path.join(app.getAppPath(), 'css', 'highcontrast.css');
        let preferencesFile: string = Swordfish.path.join(app.getPath('appData'), app.name, 'preferences.json');
        let oldCss: string = Swordfish.currentCss;
        if (existsSync(preferencesFile)) {
            try {
                let data: Buffer = readFileSync(preferencesFile);
                let json: Preferences = JSON.parse(data.toString());
                if (!json.hasOwnProperty('chatGpt')) {
                    json.chatGpt = { enabled: false, apiKey: '', model: 'o1-mini', fixTags: false };
                }
                if (!json.hasOwnProperty('anthropic')) {
                    json.anthropic = { enabled: false, apiKey: '', model: 'claude-3-5-sonnet-latest', fixTags: false };
                }
                if (!json.hasOwnProperty('caseSensitiveMatches')) {
                    json.caseSensitiveMatches = true;
                }
                if (!json.hasOwnProperty('modernmt')) {
                    json.modernmt = {
                        enabled: false,
                        apiKey: '',
                        srcLang: 'none',
                        tgtLang: 'none'
                    }
                }
                if (!json.hasOwnProperty('pageRows')) {
                    json.pageRows = 500;
                }
                if (!json.hasOwnProperty('autoConfirm')) {
                    json.autoConfirm = false;
                }
                Swordfish.currentPreferences = json;
                if (!Swordfish.currentPreferences.projectsFolder || !existsSync(Swordfish.currentPreferences.projectsFolder)) {
                    Swordfish.currentPreferences.projectsFolder = Swordfish.path.join(app.getPath('appData'), app.name, 'projects');
                    writeFileSync(Swordfish.path.join(app.getPath('appData'), app.name, 'preferences.json'), JSON.stringify(Swordfish.currentPreferences, null, 2));
                }
                if (!Swordfish.currentPreferences.memoriesFolder || !existsSync(Swordfish.currentPreferences.memoriesFolder)) {
                    Swordfish.currentPreferences.memoriesFolder = Swordfish.path.join(app.getPath('appData'), app.name, 'memories');
                    writeFileSync(Swordfish.path.join(app.getPath('appData'), app.name, 'preferences.json'), JSON.stringify(Swordfish.currentPreferences, null, 2));
                }
                if (!Swordfish.currentPreferences.glossariesFolder || !existsSync(Swordfish.currentPreferences.glossariesFolder)) {
                    Swordfish.currentPreferences.glossariesFolder = Swordfish.path.join(app.getPath('appData'), app.name, 'glossaries');
                    writeFileSync(Swordfish.path.join(app.getPath('appData'), app.name, 'preferences.json'), JSON.stringify(Swordfish.currentPreferences, null, 2));
                }
                if (!existsSync(Swordfish.currentPreferences.catalog)) {
                    Swordfish.currentPreferences.catalog = Swordfish.path.join(app.getAppPath(), 'catalog', 'catalog.xml');
                    writeFileSync(Swordfish.path.join(app.getPath('appData'), app.name, 'preferences.json'), JSON.stringify(Swordfish.currentPreferences, null, 2));
                }
                if (!existsSync(Swordfish.currentPreferences.srx)) {
                    Swordfish.currentPreferences.srx = Swordfish.path.join(app.getAppPath(), 'srx', 'default.srx');
                    writeFileSync(Swordfish.path.join(app.getPath('appData'), app.name, 'preferences.json'), JSON.stringify(Swordfish.currentPreferences, null, 2));
                }
                if (Swordfish.mainWindow) {
                    Swordfish.mainWindow.webContents.send('set-rows-page', Swordfish.currentPreferences.pageRows);
                }
            } catch (err) {
                console.error(err);
            }
        } else {
            writeFileSync(Swordfish.path.join(app.getPath('appData'), app.name, 'preferences.json'), JSON.stringify(Swordfish.currentPreferences, null, 2));
        }
        if (Swordfish.currentPreferences.theme === 'system') {
            if (nativeTheme.shouldUseDarkColors) {
                Swordfish.currentCss = dark;
            } else {
                Swordfish.currentCss = light;
            }
            if (nativeTheme.shouldUseHighContrastColors) {
                Swordfish.currentCss = highContrast;
            }
        }
        if (Swordfish.currentPreferences.theme === 'dark') {
            Swordfish.currentCss = dark;
        }
        if (Swordfish.currentPreferences.theme === 'light') {
            Swordfish.currentCss = light;
        }
        if (Swordfish.currentPreferences.theme === 'highcontrast') {
            Swordfish.currentCss = highContrast;
        }
        if ((oldCss === dark || oldCss === light) && Swordfish.currentCss === highContrast) {
            Swordfish.deleteAllTags('#C5E1A5', '#000000');
        }
        if (oldCss === highContrast && (Swordfish.currentCss === light || Swordfish.currentCss === dark)) {
            Swordfish.deleteAllTags('#009688', '#ffffff');
        }
        if (!Swordfish.currentPreferences.zoomFactor) {
            Swordfish.currentPreferences.zoomFactor = '1.0';
        }
        if (!Swordfish.currentPreferences.os) {
            Swordfish.currentPreferences.os = process.platform;
        }
    }

    static savePreferences(preferences: Preferences): void {
        if (Swordfish.preferencesWindow) {
            Swordfish.preferencesWindow.close();
        }
        let reloadProjects: boolean = this.currentPreferences.projectsFolder !== preferences.projectsFolder;
        let reloadMemories: boolean = this.currentPreferences.memoriesFolder !== preferences.memoriesFolder;
        let reloadGlossaries: boolean = this.currentPreferences.glossariesFolder !== preferences.glossariesFolder;
        writeFileSync(Swordfish.path.join(app.getPath('appData'), app.name, 'preferences.json'), JSON.stringify(preferences, null, 2));
        Swordfish.loadPreferences();
        Swordfish.setTheme();
        Swordfish.mainWindow.webContents.send('set-zoom', { zoom: Swordfish.currentPreferences.zoomFactor });
        if (reloadProjects) {
            Swordfish.mainWindow.webContents.send('request-projects', {});
        }
        if (reloadMemories) {
            Swordfish.mainWindow.webContents.send('request-memories');
        }
        if (reloadGlossaries) {
            Swordfish.mainWindow.webContents.send('request-glossaries');
        }
    }

    static showSortSegments(params: any): void {
        this.sortSegmentsWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 420,
            height: 305,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.sortParams = params;
        this.sortSegmentsWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'sortSegments.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.sortSegmentsWindow.loadURL(fileUrl.href);
        this.sortSegmentsWindow.once('ready-to-show', () => {
            this.sortSegmentsWindow.show();
        });
        this.sortSegmentsWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.sortSegmentsWindow, 'sortSegments.html');
    }

    static showFilterSegments(params: any): void {
        this.filterSegmentsWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 480,
            height: 360,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.filterParams = params;
        this.filterSegmentsWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'filterSegments.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.filterSegmentsWindow.loadURL(fileUrl.href);
        this.filterSegmentsWindow.once('ready-to-show', () => {
            this.filterParams = params;
            this.filterSegmentsWindow.show();
        });
        this.filterSegmentsWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.filterSegmentsWindow, 'filterSegments.html');
    }

    static viewProjects(): void {
        Swordfish.mainWindow.webContents.send('view-projects');
    }

    static closeSelectedTab(): void {
        Swordfish.mainWindow.webContents.send('close-tab');
    }

    static editProject(): void {
        Swordfish.mainWindow.webContents.send('edit-project');
    }

    static showAddProject(): void {
        this.addProjectWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 980,
            height: 570,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.addProjectWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'addProject.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.addProjectWindow.loadURL(fileUrl.href);
        this.addProjectWindow.once('ready-to-show', () => {
            this.addProjectWindow.show();
        });
        this.addProjectWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.addProjectWindow, 'addProject.html');
    }

    static showEditProject(project: Project): void {
        Swordfish.editedProject = project;
        this.editProjectWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 920,
            height: 240,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.editProjectWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'editProject.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.editProjectWindow.loadURL(fileUrl.href);
        this.editProjectWindow.once('ready-to-show', () => {
            this.editProjectWindow.show();
        });
        this.editProjectWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.editProjectWindow, 'editProject.html');
    }

    static exportOpenProject(arg: any): void {
        Swordfish.sendRequest('/projects/get', arg,
            (data: any) => {
                Swordfish.exportProjectTranslations(data);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static exportProjectTranslations(project: any): void {
        if (project.files.length === 1 && project.files[0].type !== 'DITA Map') {
            let parsed: any = Swordfish.getSaveName(project.files[0], project.targetLang);
            dialog.showSaveDialog(Swordfish.mainWindow, {
                defaultPath: parsed.defaultPath,
                filters: parsed.filters,
                properties: ['createDirectory', 'showOverwriteConfirmation']
            }).then((value: Electron.SaveDialogReturnValue) => {
                if (!value.canceled) {
                    Swordfish.sendRequest('/projects/translations', { project: project.id, output: value.filePath },
                        (data: any) => {
                            Swordfish.exportTranslations(data, value.filePath, true);
                        }, (reason: string) => {
                            Swordfish.showMessage({ type: 'error', message: reason });
                        }
                    );
                }
            }).catch((error: Error) => {
                console.error(error.message);
            });
        } else {
            dialog.showOpenDialog(Swordfish.mainWindow, {
                title: 'Select folder',
                properties: ['createDirectory', 'openDirectory']
            }).then((value: Electron.OpenDialogReturnValue) => {
                if (!value.canceled) {
                    Swordfish.sendRequest('/projects/translations', { project: project.id, output: value.filePaths[0] },
                        (data: any) => {
                            Swordfish.exportTranslations(data, value.filePaths[0], false);
                        }, (reason: string) => {
                            Swordfish.showMessage({ type: 'error', message: reason });
                        }
                    );
                }
            }).catch((error: Error) => {
                console.error(error.message);
            });
        }
    }

    static exportTranslations(data: any, output: string, isFile: boolean): void {
        if (data.status !== Swordfish.SUCCESS) {
            Swordfish.showMessage({ type: 'error', message: data.reason });
        }
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Exporting translations');
        Swordfish.currentStatus = data;
        let processId: string = data.process;
        let intervalObject = setInterval(() => {
            if (Swordfish.currentStatus.progress) {
                if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                    Swordfish.mainWindow.webContents.send('end-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', '');
                    clearInterval(intervalObject);
                    if (isFile) {
                        dialog.showMessageBox(Swordfish.mainWindow, {
                            type: 'question',
                            message: 'Translations exported.\n\nOpen translated file?',
                            buttons: ['Yes', 'No']
                        }).then((selection: Electron.MessageBoxReturnValue) => {
                            if (selection.response === 0) {
                                shell.openExternal('file://' + output).catch(() => {
                                    shell.openPath(output).catch((reason: any) => {
                                        if (reason instanceof Error) {
                                            console.error(reason.message);
                                        }
                                        this.showMessage({ type: 'error', message: 'Unable to open translated file.' });
                                    });
                                });
                            }
                        });
                    } else {
                        Swordfish.showMessage({ type: 'info', message: 'Translations exported.' });
                    }
                    return;
                } else if (Swordfish.currentStatus.progress === Swordfish.PROCESSING) {
                    // it's OK, keep waiting
                } else if (Swordfish.currentStatus.progress === Swordfish.ERROR) {
                    Swordfish.mainWindow.webContents.send('end-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', '');
                    clearInterval(intervalObject);
                    Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                    return;
                } else {
                    Swordfish.mainWindow.webContents.send('end-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', '');
                    clearInterval(intervalObject);
                    Swordfish.showMessage({ type: 'error', message: 'Unknown error exporting translations' });
                    return;
                }
            }
            Swordfish.getProjectsProgress(processId);
        }, 500);
    }

    static getSaveName(file: any, lang: string): any {
        let fileName: string = file.file;
        if (fileName.endsWith('.sdlppx')) {
            return {
                defaultPath: fileName.substring(0, fileName.lastIndexOf('.')) + '.sdlrpx',
                filters: [{ name: file.type, extensions: 'sdlrpx' }, { name: 'Any File', extensions: '*' }]
            }
        }
        let name = fileName.substring(0, fileName.lastIndexOf('.'));
        let extension = fileName.substring(fileName.lastIndexOf('.'));
        return {
            defaultPath: name + '_' + lang + extension,
            filters: [{ name: file.type, extensions: extension }, { name: 'Any File', extensions: '*' }]
        }
    }

    static addFile(): void {
        let extensions: string[] = ['inx', 'icml', 'idml', 'ditamap', 'dita', 'xml', 'html', 'htm', 'js', 'properties', 'json', 'mif', 'docx', 'xlsx', 'pptx',
            'sxw', 'sxc', 'sxi', 'sxd', 'odt', 'ods', 'odp', 'odg', 'txt', 'po', 'pot', 'rc', 'resx', 'sdlxliff', 'srt', 'svg', 'sdlppx', 'ts', 'txml', 'vsdx',
            'xlf', 'xliff', 'mqxliff', 'txlf'];
        let filters: any[] = [
            { name: 'Supported Files', extensions: extensions },
            { name: 'Any File', extensions: ['*'] }
        ];
        dialog.showOpenDialog(Swordfish.mainWindow, {
            properties: ['openFile'],
            filters: filters
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                Swordfish.selectedFiles = value.filePaths;
                this.addFileWindow = new BrowserWindow({
                    parent: this.mainWindow,
                    width: 890,
                    height: 360,
                    minimizable: false,
                    maximizable: false,
                    resizable: false,
                    show: false,
                    icon: this.iconPath,
                    webPreferences: {
                        nodeIntegration: true,
                        contextIsolation: false
                    }
                });
                this.addFileWindow.setMenu(null);
                let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'addFile.html');
                let fileUrl: URL = new URL('file://' + filePath);
                this.addFileWindow.loadURL(fileUrl.href);
                this.addFileWindow.once('ready-to-show', () => {
                    this.addFileWindow.show();
                });
                this.addFileWindow.on('close', () => {
                    this.mainWindow.focus();
                });
                Swordfish.setLocation(this.addFileWindow, 'addFile.html');
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    static setSelectedFile(event: IpcMainEvent): void {
        if (Swordfish.selectedFiles.length > 0) {
            Swordfish.getFileType(event, Swordfish.selectedFiles);
            Swordfish.selectedFiles = [];
        } else {
            Swordfish.showMessage({ type: 'error', message: 'No file selected' });
        }
    }

    static translateProjects(): void {
        Swordfish.mainWindow.webContents.send('translate-projects');
    }

    static updateProject(data: any) {
        Swordfish.sendRequest('/projects/update', data,
            (data: any) => {
                if (data.status === Swordfish.SUCCESS) {
                    Swordfish.mainWindow.webContents.send('request-projects', {});
                    Swordfish.editProjectWindow.close();
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static createProject(arg: any): void {
        if (arg.from === 'addProject') {
            Swordfish.addProjectWindow.close();
        }
        if (arg.from === 'addFile') {
            Swordfish.addFileWindow.close();
        }
        arg.xmlfilter = Swordfish.path.join(app.getAppPath(), 'xmlfilter');
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Creating project...');
        Swordfish.sendRequest('/projects/create', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.mainWindow.webContents.send('end-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', '');
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
                Swordfish.currentStatus = data;
                let processId: string = data.process;
                let intervalObject = setInterval(() => {
                    if (Swordfish.currentStatus.progress) {
                        if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            clearInterval(intervalObject);
                            Swordfish.mainWindow.webContents.send('request-projects', { open: processId });
                            return;
                        } else if (Swordfish.currentStatus.progress === Swordfish.PROCESSING) {
                            // it's OK, keep waiting
                        } else if (Swordfish.currentStatus.progress === Swordfish.ERROR) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            clearInterval(intervalObject);
                            Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                            return;
                        } else {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            clearInterval(intervalObject);
                            Swordfish.showMessage({ type: 'error', message: 'Unknown error processing files' });
                            return;
                        }
                    }
                    Swordfish.getProjectsProgress(processId);
                }, 500);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getProjectsProgress(process: string): void {
        this.sendRequest('/projects/status', { process: process },
            (data: any) => {
                Swordfish.currentStatus = data;
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getProjects(event: IpcMainEvent): void {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Loading projects');
        Swordfish.sendRequest('/projects/list', {},
            (data: any) => {
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.mainWindow.webContents.send('end-waiting');
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                event.sender.send('set-projects', data.projects);
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getMemories(event: IpcMainEvent): void {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Loading memories');
        Swordfish.sendRequest('/memories/list', {},
            (data: any) => {
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.mainWindow.webContents.send('end-waiting');
                if (data.status === Swordfish.SUCCESS) {
                    event.sender.send('set-memories', data.memories);
                } else {
                    dialog.showMessageBox({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('set-status', '');
                dialog.showMessageBox({ type: 'error', message: reason });

            }
        );
    }

    static getGlossaries(event: IpcMainEvent): void {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Loading glossaries');
        Swordfish.sendRequest('/glossaries/list', {},
            (data: any) => {
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.mainWindow.webContents.send('end-waiting');
                if (data.status === Swordfish.SUCCESS) {
                    event.sender.send('set-glossaries', data.glossaries);
                } else {
                    dialog.showMessageBox({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('set-status', '');
                dialog.showMessageBox({ type: 'error', message: reason });

            }
        );
    }

    selectSourceFiles(event: IpcMainEvent): void {
        let extensions: string[] = ['inx', 'icml', 'idml', 'ditamap', 'dita', 'xml', 'html', 'htm', 'js', 'properties', 'json', 'mif', 'docx', 'xlsx', 'pptx',
            'sxw', 'sxc', 'sxi', 'sxd', 'odt', 'ods', 'odp', 'odg', 'txt', 'po', 'pot', 'rc', 'resx', 'sdlxliff', 'srt', 'svg', 'sdlppx', 'ts', 'txml', 'vsdx',
            'xlf', 'xliff', 'mqxliff', 'txlf'];
        let filters: any[] = [
            { name: 'Supported Files', extensions: extensions },
            { name: 'Any File', extensions: ['*'] },
        ];
        dialog.showOpenDialog({
            properties: ['openFile', 'multiSelections'],
            filters: filters
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                Swordfish.getFileType(event, value.filePaths);
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    static getSelectedFiles(event: IpcMainEvent): void {
        if (Swordfish.selectedFiles?.length > 0) {
            Swordfish.getFileType(event, Swordfish.selectedFiles);
            Swordfish.selectedFiles = [];
        }
    }

    static getFileType(event: IpcMainEvent, files: string[]): void {
        Swordfish.sendRequest('/services/getFileType', { files: files },
            (data: any) => {
                event.sender.send('add-source-files', data.files);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    getLanguages(event: IpcMainEvent): void {
        Swordfish.sendRequest('/services/getLanguages', {},
            (data: any) => {
                data.srcLang = Swordfish.currentPreferences.srcLang;
                data.tgtLang = Swordfish.currentPreferences.tgtLang;
                event.sender.send('set-languages', data);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    getMtLanguages(event: IpcMainEvent): void {
        let mtManager: MTManager = new MTManager(Swordfish.currentPreferences, '', '');
        event.sender.send('set-mt-languages', mtManager.getMTLanguages());
    }

    static viewMemories(): void {
        Swordfish.mainWindow.webContents.send('view-memories');
    }

    static showServerSettings(type: string): void {
        this.serverSettingsWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 440,
            height: 240,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.typeParam = type;
        this.serverSettingsWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'serverSettings.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.serverSettingsWindow.loadURL(fileUrl.href);
        this.serverSettingsWindow.once('ready-to-show', () => {
            this.serverSettingsWindow.show();
        });
        this.serverSettingsWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.serverSettingsWindow, 'serverSettings.html');
    }

    static showBrowseDatabases() {
        this.browseDatabasesWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 635,
            height: 355,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.browseDatabasesWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'browseDatabases.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.browseDatabasesWindow.loadURL(fileUrl.href);
        this.browseDatabasesWindow.once('ready-to-show', () => {
            this.browseDatabasesWindow.show();
        });
        this.browseDatabasesWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.browseDatabasesWindow, 'browseDatabases.html');
    }

    static connectToServer(args: any): void {
        Swordfish.sendRequest('/services/remoteDatabases', args,
            (data: any) => {
                if (data.status === Swordfish.SUCCESS) {
                    this.remoteTmParams = {};
                    this.remoteTmParams.server = args.server;
                    this.remoteTmParams.user = args.user;
                    this.remoteTmParams.password = args.password;
                    this.remoteTmParams.memories = data.memories;
                    this.remoteTmParams.type = this.typeParam;
                    this.showBrowseDatabases();
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static addDatabases(args: any): void {
        Swordfish.sendRequest('/services/addDatabases', args,
            (data: any) => {
                if (data.status === Swordfish.SUCCESS) {
                    if (args.type === 'memory') {
                        Swordfish.mainWindow.webContents.send('request-memories');
                        Swordfish.showMessage({ type: 'info', message: 'Memory added' });
                    } else {
                        Swordfish.mainWindow.webContents.send('request-glossaries');
                        Swordfish.showMessage({ type: 'info', message: 'Glossary added' });
                    }
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static showAddMemory(): void {
        this.addMemoryWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 435,
            height: 290,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.addMemoryWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'addMemory.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.addMemoryWindow.loadURL(fileUrl.href);
        this.addMemoryWindow.once('ready-to-show', () => {
            this.addMemoryWindow.show();
        });
        this.addMemoryWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.addMemoryWindow, 'addMemory.html');
    }

    static viewGlossaries(): void {
        Swordfish.mainWindow.webContents.send('view-glossaries');
    }

    static sendRequest(url: string, params: any, success: Function, error: Function) {
        let options: any = {
            url: 'http://127.0.0.1:8070' + url,
            method: 'POST'
        }
        let request: ClientRequest = net.request(options);
        let responseData: string = '';
        request.setHeader('Content-Type', 'application/json');
        request.setHeader('Accept', 'application/json');
        request.on('response', (response: IncomingMessage) => {
            response.on('error', (e: Error) => {
                error(e.message);
            });
            response.on('aborted', () => {
                error('Request aborted');
            });
            response.on('end', () => {
                try {
                    let json = JSON.parse(responseData);
                    success(json);
                } catch (reason: any) {
                    error(JSON.stringify(reason));
                }
            });
            response.on('data', (chunk: Buffer) => {
                responseData += chunk.toString();
            });
        });
        request.write(JSON.stringify(params));
        request.end();
    }

    static showHelp(): void {
        shell.openExternal('file://' + this.path.join(app.getAppPath(), 'swordfish.pdf')).catch(() => {
            shell.openPath(this.path.join(app.getAppPath(), 'swordfish.pdf')).catch((reason: any) => {
                if (reason instanceof Error) {
                    console.error(reason.message);
                }
                this.showMessage({ type: 'error', message: 'Unable to open Swordfish User Guide.' });
            });
        });
    }

    static showAbout(): void {
        Swordfish.aboutWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 360,
            height: 490,
            resizable: false,
            minimizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        Swordfish.aboutWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'about.html');
        let fileUrl: URL = new URL('file://' + filePath);
        Swordfish.aboutWindow.loadURL(fileUrl.href);
        Swordfish.aboutWindow.once('ready-to-show', () => {
            Swordfish.aboutWindow.show();
        });
        Swordfish.aboutWindow.on('close', () => {
            Swordfish.mainWindow.focus();
        });
    }

    static openLicense(type: string) {
        let licenseFile = '';
        let title = '';
        switch (type) {
            case 'Swordfish':
            case "OpenXLIFF":
            case "XMLJava":
            case "BCP47J":
                licenseFile = 'EclipsePublicLicense1.0.html';
                title = 'Eclipse Public License 1.0';
                break;
            case "electron":
                licenseFile = 'electron.txt';
                title = 'MIT License';
                break;
            case "MapDB":
                licenseFile = 'Apache2.0.html';
                title = 'Apache 2.0';
                break;
            case "Java":
                licenseFile = 'java.html';
                title = 'GPL2 with Classpath Exception';
                break;
            case "jsoup":
                licenseFile = 'jsoup.txt';
                title = 'MIT License';
                break;
            default:
                Swordfish.showMessage({ type: 'error', message: 'Unknown license' });
                return;
        }
        let licenseWindow = new BrowserWindow({
            parent: this.licensesWindow,
            width: 680,
            height: 400,
            show: false,
            title: title,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        licenseWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'licenses', licenseFile);
        let fileUrl: URL = new URL('file://' + filePath);
        licenseWindow.loadURL(fileUrl.href);
        licenseWindow.once('ready-to-show', () => {
            licenseWindow.show();
        });
        licenseWindow.on('close', () => {
            this.licensesWindow.focus();
        });
        licenseWindow.webContents.on('did-finish-load', () => {
            let css: string = readFileSync(Swordfish.currentCss.substring('file://'.length), { encoding: 'utf8' });
            licenseWindow.webContents.insertCSS(css.toString());
        });
    }

    static showPreferences(): void {
        this.mainWindow.webContents.send('start-waiting');
        this.preferencesWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 640,
            height: 340,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.preferencesWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'preferencesDialog.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.preferencesWindow.loadURL(fileUrl.href);
        this.preferencesWindow.once('ready-to-show', () => {
            let mtManager: MTManager = new MTManager(Swordfish.currentPreferences, '', '');
            this.preferencesWindow.webContents.send('set-mt-languages', mtManager.getMTLanguages());
            this.preferencesWindow.show();
        });
        this.preferencesWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.preferencesWindow, 'preferencesDialog.html');
    }

    static showSystemInfo() {
        this.systemInfoWindow = new BrowserWindow({
            parent: Swordfish.aboutWindow,
            width: 430,
            height: 240,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.systemInfoWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'systemInfo.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.systemInfoWindow.loadURL(fileUrl.href);
        this.systemInfoWindow.once('ready-to-show', () => {
            this.systemInfoWindow.show();
        });
        this.systemInfoWindow.on('close', () => {
            Swordfish.aboutWindow.focus();
        });
        Swordfish.setLocation(this.systemInfoWindow, 'systemInfo.html');
    }

    static getSystemInformation(event: IpcMainEvent) {
        this.sendRequest('/services/systemInfo', {},
            (data: any) => {
                if (data.status === Swordfish.SUCCESS) {
                    data.electron = process.versions.electron;
                    event.sender.send('set-system-info', data);
        } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static showLicenses(arg: any): void {
        let parent: BrowserWindow = Swordfish.mainWindow;
        if (arg.from === 'about' && Swordfish.aboutWindow) {
            parent = Swordfish.aboutWindow;
        }
        this.licensesWindow = new BrowserWindow({
            parent: parent,
            width: 425,
            height: 410,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.licensesWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'licenses.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.licensesWindow.loadURL(fileUrl.href);
        this.licensesWindow.once('ready-to-show', () => {
            this.licensesWindow.show();
        });
        this.licensesWindow.on('close', () => {
            parent.focus();
        });
        Swordfish.setLocation(this.licensesWindow, 'licenses.html');
    }

    static showReleaseHistory(): void {
        shell.openExternal('https://www.maxprograms.com/products/swfishlog.html').catch((reason: any) => {
            if (reason instanceof Error) {
                console.error(reason.message);
            }
            this.showMessage({ type: 'error', message: 'Unable to open release history.' });
        });
    }

    static showSupportGroup(): void {
        shell.openExternal('https://groups.io/g/maxprograms/').catch((reason: any) => {
            if (reason instanceof Error) {
                console.error(reason.message);
            }
            this.showMessage({ type: 'error', message: 'Unable to open support group page.' });
        });
    }

    static setTheme(): void {
        Swordfish.mainWindow.webContents.send('request-theme');
    }

    static checkUpdates(silent: boolean): void {
        session.defaultSession.clearCache().then(() => {
            let request: Electron.ClientRequest = net.request({
                url: 'https://maxprograms.com/swordfish.json',
                session: session.defaultSession
            });
            request.on('response', (response: IncomingMessage) => {
                let responseData: string = '';
                if (response.statusCode !== 200) {
                    if (!silent) {
                        Swordfish.showMessage({
                            type: 'info',
                            message: 'Server status: ' + response.statusCode
                        });
                    }
                }
                response.on('data', (chunk: Buffer) => {
                    responseData += chunk;
                });
                response.on('end', () => {
                    try {
                        let parsedData = JSON.parse(responseData);
                        if (app.getVersion() !== parsedData.version) {
                            Swordfish.latestVersion = parsedData.version;
                            switch (process.platform) {
                                case 'darwin':
                                    Swordfish.downloadLink = process.arch === 'arm64' ? parsedData.arm64 : parsedData.darwin;
                                    break;
                                case 'win32':
                                    Swordfish.downloadLink = parsedData.win32;
                                    break;
                                case 'linux':
                                    Swordfish.downloadLink = parsedData.linux;
                                    break;
                            }
                            Swordfish.updatesWindow = new BrowserWindow({
                                parent: this.mainWindow,
                                width: 560,
                                height: 240,
                                minimizable: false,
                                maximizable: false,
                                resizable: false,
                                show: false,
                                icon: this.iconPath,
                                webPreferences: {
                                    nodeIntegration: true,
                                    contextIsolation: false
                                }
                            });
                            Swordfish.updatesWindow.setMenu(null);
                            let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'updates.html');
                            let fileUrl: URL = new URL('file://' + filePath);
                            Swordfish.updatesWindow.loadURL(fileUrl.href);
                            Swordfish.updatesWindow.once('ready-to-show', () => {
                                Swordfish.updatesWindow.show();
                            });
                            this.updatesWindow.on('close', () => {
                                this.mainWindow.focus();
                            });
                        } else if (!silent) {
                            Swordfish.showMessage({
                                type: 'info',
                                message: 'There are currently no updates available'
                            });
                        }
                    } catch (reason: any) {
                        if (!silent) {
                            Swordfish.showMessage({
                                type: 'error',
                                message: reason.message
                            });
                        }
                    }
                });
            });
            request.on('error', (error: Error) => {
                if (!silent) {
                    Swordfish.showMessage({
                        type: 'error',
                        message: error.message
                    });
                }
            });
            request.end();
        });
    }

    getTypes(event: IpcMainEvent): void {
        Swordfish.sendRequest('/services/getFileTypes', {},
            (data: any) => {
                if (data.status === Swordfish.SUCCESS) {
                    event.sender.send('set-types', data);
        } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    getCharset(event: IpcMainEvent): void {
        Swordfish.sendRequest('/services/getCharsets', {},
            (data: any) => {
                if (data.status === Swordfish.SUCCESS) {
                    event.sender.send('set-charsets', data);
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    getClients(event: IpcMainEvent): void {
        Swordfish.sendRequest('/services/getClients', {},
            (data: any) => {
                if (data.status === Swordfish.SUCCESS) {
                    event.sender.send('set-clients', data.clients);
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    getProjectNames(event: IpcMainEvent): void {
        Swordfish.sendRequest('/services/getProjects', {},
            (data: any) => {
                if (data.status === Swordfish.SUCCESS) {
                    event.sender.send('set-project-names', data.projects);
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    getSubjects(event: IpcMainEvent): void {
        Swordfish.sendRequest('/services/getSubjects', {},
            (data: any) => {
                if (data.status === Swordfish.SUCCESS) {
                    event.sender.send('set-subjects', data.subjects);
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    browseSRX(event: IpcMainEvent): void {
        dialog.showOpenDialog({
            title: 'Default SRX File',
            defaultPath: Swordfish.currentPreferences.srx,
            properties: ['openFile'],
            filters: [
                { name: 'SRX File', extensions: ['srx'] },
                { name: 'Any File', extensions: ['*'] }
            ]
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                event.sender.send('set-srx', value.filePaths[0]);
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    browseProjects(event: IpcMainEvent): void {
        dialog.showOpenDialog({
            title: 'Projects Folder',
            defaultPath: Swordfish.currentPreferences.projectsFolder,
            properties: ['openDirectory', 'createDirectory']
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                event.sender.send('set-projects-folder', value.filePaths[0]);
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    browseMemories(event: IpcMainEvent): void {
        dialog.showOpenDialog({
            title: 'Memories Folder',
            defaultPath: Swordfish.currentPreferences.memoriesFolder,
            properties: ['openDirectory', 'createDirectory']
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                event.sender.send('set-memories-folder', value.filePaths[0]);
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    browseGlossaries(event: IpcMainEvent): void {
        dialog.showOpenDialog({
            title: 'Glossaries Folder',
            defaultPath: Swordfish.currentPreferences.glossariesFolder,
            properties: ['openDirectory', 'createDirectory']
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                event.sender.send('set-glossaries-folder', value.filePaths[0]);
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    browseCatalog(event: IpcMainEvent): void {
        dialog.showOpenDialog({
            title: 'Default Catalog',
            defaultPath: Swordfish.currentPreferences.catalog,
            properties: ['openFile'],
            filters: [
                { name: 'XML File', extensions: ['xml'] },
                { name: 'Any File', extensions: ['*'] }
            ]
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                event.sender.send('set-catalog', value.filePaths[0]);
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    static getDefaultLanguages(): void {
        this.defaultLangsWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 600,
            height: 190,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.defaultLangsWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'defaultLangs.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.defaultLangsWindow.loadURL(fileUrl.href);
        this.defaultLangsWindow.once('ready-to-show', () => {
            this.defaultLangsWindow.show();
        });
        this.defaultLangsWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.defaultLangsWindow, 'defaultLangs.html');
    }

    static savelanguages(arg: any) {
        this.defaultLangsWindow.close();
        this.currentPreferences.srcLang = arg.srcLang;
        this.currentPreferences.tgtLang = arg.tgtLang;
        writeFileSync(Swordfish.path.join(app.getPath('appData'), app.name, 'preferences.json'), JSON.stringify(this.currentPreferences, null, 2));
    }

    static getSegmenstCount(event: IpcMainEvent, arg: any): void {
        Swordfish.sendRequest('/projects/count', arg,
            (data: any) => {
                if (data.status === Swordfish.SUCCESS) {
                    data.project = arg.project;
                    event.sender.send('set-segments-count', data);
                    Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics });
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getSegments(event: IpcMainEvent, arg: any): void {
        Swordfish.sendRequest('/projects/segments', arg,
            (data: any) => {
                if (data.status === Swordfish.SUCCESS) {
                    data.project = arg.project;
                    event.sender.send('set-segments', data);
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static removeProjects(arg: any) {
        dialog.showMessageBox(Swordfish.mainWindow, { type: "question", message: "Delete selected projects?", buttons: ["Yes", "No"], defaultId: 1 }
        ).then((result: any) => {
            if (result.response === 0) {
                Swordfish.sendRequest('/projects/delete', arg,
                    (data: any) => {
                        if (data.status === Swordfish.SUCCESS) {
                            Swordfish.mainWindow.webContents.send('request-projects', {});
                        } else {
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                        }
                    },
                    (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static addMemory(arg: any): void {
        Swordfish.addMemoryWindow.close();
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Creating memory');
        Swordfish.sendRequest('/memories/create', arg,
            (data: any) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
            return;
        }
                Swordfish.mainWindow.webContents.send('request-memories');
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static addGlossary(arg: any): void {
        Swordfish.addGlossaryWindow.close();
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Creating glossary');
        Swordfish.sendRequest('/glossaries/create', arg,
            (data: any) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
            return;
        }
                Swordfish.mainWindow.webContents.send('request-glossaries');
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static showImportTMX(memory: string): void {
        this.importTmxWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 600,
            height: 290,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.memoryParam = memory;
        this.importTmxWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'importTmx.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.importTmxWindow.loadURL(fileUrl.href);
        this.importTmxWindow.once('ready-to-show', () => {
            this.importTmxWindow.show();
        });
        this.importTmxWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.importTmxWindow, 'importTmx.html');
    }

    static showImportGlossary(glossary: string): void {
        this.importGlossaryWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 600,
            height: 290,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.glossaryParam = glossary;
        this.importGlossaryWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'importGlossary.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.importGlossaryWindow.loadURL(fileUrl.href);
        this.importGlossaryWindow.once('ready-to-show', () => {
            this.importGlossaryWindow.show();
        });
        this.importGlossaryWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.importGlossaryWindow, 'importGlossary.html');
    }

    static importTmxFile(arg: any): void {
        Swordfish.importTmxWindow.close();
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Importing TMX File');
        Swordfish.sendRequest('/memories/import', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.mainWindow.webContents.send('end-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', '');
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
                Swordfish.currentStatus = data;
                let processId: string = data.process;
                let intervalObject = setInterval(() => {
                    if (Swordfish.currentStatus.imported) {
                        Swordfish.mainWindow.webContents.send('set-status', 'Imported ' + Swordfish.currentStatus.imported + ' units');
                    }
                    if (Swordfish.currentStatus.status === Swordfish.SUCCESS) {
                        if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            clearInterval(intervalObject);
                            Swordfish.showMessage({ type: 'info', message: 'Imported ' + Swordfish.currentStatus.imported + ' segments.' });
                            return;
                        }
                    }
                    if (Swordfish.currentStatus.status === Swordfish.ERROR) {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        clearInterval(intervalObject);
                        Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                        return;
                    }
                    Swordfish.getMemoriesProgress(processId);
                }, 2500);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getMemoriesProgress(process: string): void {
        this.sendRequest('/memories/status', { process: process },
            (data: any) => {
                Swordfish.currentStatus = data;
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getGlossariesProgress(process: string): void {
        this.sendRequest('/glossaries/status', { process: process },
            (data: any) => {
                Swordfish.currentStatus = data;
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    getTmxFile(event: IpcMainEvent): void {
        dialog.showOpenDialog({
            properties: ['openFile'],
            filters: [
                { name: 'TMX File', extensions: ['tmx'] },
                { name: 'Any File', extensions: ['*'] }
            ]
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                event.sender.send('set-tmx-file', value.filePaths[0]);
            }
        });
    }

    static getGlossaryFile(event: IpcMainEvent): void {
        dialog.showOpenDialog({
            properties: ['openFile'],
            filters: [
                { name: 'TMX/TBX File', extensions: ['tmx', 'tbx'] },
                { name: 'Any File', extensions: ['*'] }
            ]
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                event.sender.send('set-glossary-file', value.filePaths[0]);
            }
        });
    }

    static removeMemory(): void {
        Swordfish.mainWindow.webContents.send('remove-memory');
    }

    static removeMemories(arg: string[]) {
        dialog.showMessageBox(Swordfish.mainWindow, { type: "question", message: "Delete selected memories?", buttons: ["Yes", "No"], defaultId: 1 }
        ).then((result: any) => {
            if (result.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Removing memories');
                Swordfish.sendRequest('/memories/delete', { memories: arg },
            (data: any) => {
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                        }
                        Swordfish.currentStatus = data;
                        let processId: string = data.process;
                        let intervalObject = setInterval(() => {
                            if (Swordfish.currentStatus.status === Swordfish.SUCCESS) {
                                if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.mainWindow.webContents.send('request-memories');
                                    return;
                                }
                            }
                            if (Swordfish.currentStatus.status === Swordfish.ERROR) {
                                Swordfish.mainWindow.webContents.send('end-waiting');
                                Swordfish.mainWindow.webContents.send('set-status', '');
                                clearInterval(intervalObject);
                                Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                                return;
                            }
                            Swordfish.getMemoriesProgress(processId);
                        }, 500);
            },
            (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static removeGlossaries(arg: string[]) {
        dialog.showMessageBox(Swordfish.mainWindow, { type: "question", message: "Delete selected glossaries?", buttons: ["Yes", "No"], defaultId: 1 }
        ).then((result: any) => {
            if (result.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Removing glossaries');
                Swordfish.sendRequest('/glossaries/delete', { glossaries: arg },
                    (data: any) => {
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                        }
                        Swordfish.currentStatus = data;
                        let processId: string = data.process;
                        let intervalObject = setInterval(() => {
                            if (Swordfish.currentStatus.status === Swordfish.SUCCESS) {
                                if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.mainWindow.webContents.send('request-glossaries');
                                    return;
                                }
                            }
                            if (Swordfish.currentStatus.status === Swordfish.ERROR) {
                                Swordfish.mainWindow.webContents.send('end-waiting');
                                Swordfish.mainWindow.webContents.send('set-status', '');
                                clearInterval(intervalObject);
                                Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                                return;
                            }
                            Swordfish.getGlossariesProgress(processId);
                        }, 500);
                    },
                    (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static exportMemories(memories: any[]): void {
        if (memories.length === 1) {
            dialog.showSaveDialog(Swordfish.mainWindow, {
                defaultPath: memories[0].name + '.tmx',
                filters: [{ name: 'TMX Files', extensions: ['tmx'] }, { name: 'Any File', extensions: ['*'] }],
                properties: ['createDirectory', 'showOverwriteConfirmation']
            }).then((value: Electron.SaveDialogReturnValue) => {
                if (!value.canceled) {
                    Swordfish.mainWindow.webContents.send('start-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', 'Exporting memories');
                    Swordfish.sendRequest('/memories/export', { memory: memories[0].memory, tmx: value.filePath },
                        (data: any) => {
                            if (data.status !== Swordfish.SUCCESS) {
                                Swordfish.mainWindow.webContents.send('end-waiting');
                                Swordfish.mainWindow.webContents.send('set-status', '');
                                Swordfish.showMessage({ type: 'error', message: data.reason });
                            }
                            Swordfish.currentStatus = data;
                            let processId: string = data.process;
                            let intervalObject = setInterval(() => {
                                if (Swordfish.currentStatus.status === Swordfish.SUCCESS) {
                                    if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                                        Swordfish.mainWindow.webContents.send('end-waiting');
                                        Swordfish.mainWindow.webContents.send('set-status', '');
                                        Swordfish.showMessage({ type: 'info', message: 'Memories exported' });
                                        clearInterval(intervalObject);
            return;
        }
                                }
                                if (Swordfish.currentStatus.status === Swordfish.ERROR) {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                                    return;
                                }
                                Swordfish.getMemoriesProgress(processId);
                            }, 500);
                        }, (reason: string) => {
                            Swordfish.showMessage({ type: 'error', message: reason });
                        }
                    );
                }
            }).catch((error: Error) => {
                console.error(error.message);
            });
        } else {
            Swordfish.showMessage({ type: 'warning', message: 'Select one memory' });
        }
    }

    static exportGlossaries(glossaries: any[]): void {
        if (glossaries.length === 1) {
            dialog.showSaveDialog(Swordfish.mainWindow, {
                defaultPath: glossaries[0].name + '.tmx',
                filters: [{ name: 'TMX Files', extensions: ['tmx'] }, { name: 'Any File', extensions: ['*'] }],
                properties: ['createDirectory', 'showOverwriteConfirmation']
            }).then((value: Electron.SaveDialogReturnValue) => {
                if (!value.canceled) {
                    Swordfish.mainWindow.webContents.send('start-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', 'Exporting glossaries');
                    Swordfish.sendRequest('/glossaries/export', { glossary: glossaries[0].glossary, file: value.filePath },
            (data: any) => {
                            if (data.status !== Swordfish.SUCCESS) {
                                Swordfish.mainWindow.webContents.send('end-waiting');
                                Swordfish.mainWindow.webContents.send('set-status', '');
                                Swordfish.showMessage({ type: 'error', message: data.reason });
                            }
                            Swordfish.currentStatus = data;
                            let processId: string = data.process;
                            let intervalObject = setInterval(() => {
                                if (Swordfish.currentStatus.status === Swordfish.SUCCESS) {
                                    if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                                        Swordfish.mainWindow.webContents.send('end-waiting');
                                        Swordfish.mainWindow.webContents.send('set-status', '');
                                        clearInterval(intervalObject);
                                        Swordfish.showMessage({ type: 'info', message: 'Glossaries exported' });
                                        return;
                                    }
                                }
                                if (Swordfish.currentStatus.status === Swordfish.ERROR) {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                                    return;
                                }
                                Swordfish.getGlossariesProgress(processId);
                            }, 500);
                        }, (reason: string) => {
                            Swordfish.showMessage({ type: 'error', message: reason });
                        }
                    );
                }
            }).catch((error: Error) => {
                console.error(error.message);
            });
        } else {
            Swordfish.showMessage({ type: 'warning', message: 'Select one glossary' });
        }
    }

    static closeProject(arg: any): void {
        Swordfish.sendRequest('/projects/close', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static saveTranslation(arg: any): void {
        Swordfish.sendRequest('/projects/save', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
            return;
        }
                if (data.propagated.length > 0) {
                    Swordfish.mainWindow.webContents.send('auto-propagate', { project: arg.project, rows: data.propagated });
                }
                if (data.tagErrors || data.spaceErrors) {
                    Swordfish.mainWindow.webContents.send('set-errors', {
                        project: arg.project,
                        file: arg.file,
                        unit: arg.unit,
                        segment: arg.segment,
                        tagErrors: data.tagErrors,
                        spaceErrors: data.spaceErrors
                    });
                } else {
                    Swordfish.mainWindow.webContents.send('clear-errors', {
                        project: arg.project,
                        file: arg.file,
                        unit: arg.unit,
                        segment: arg.segment,
                        tagErrors: data.tagErrors,
                        spaceErrors: data.spaceErrors
                    });
                }
                Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics });
                if (arg.translation !== data.target) {
                    Swordfish.mainWindow.webContents.send('update-target', {
                        project: arg.project,
                        file: arg.file,
                        unit: arg.unit,
                        segment: arg.segment,
                        target: data.target
                    });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static saveSource(arg: any): void {
        Swordfish.sendRequest('/projects/saveSource', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                if (data.tagErrors || data.spaceErrors) {
                    Swordfish.mainWindow.webContents.send('set-errors', {
                        project: arg.project,
                        file: arg.file,
                        unit: arg.unit,
                        segment: arg.segment,
                        tagErrors: data.tagErrors,
                        spaceErrors: data.spaceErrors
                    });
                } else {
                    Swordfish.mainWindow.webContents.send('clear-errors', {
                        project: arg.project,
                        file: arg.file,
                        unit: arg.unit,
                        segment: arg.segment,
                        tagErrors: data.tagErrors,
                        spaceErrors: data.spaceErrors
                    });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getMatches(arg: any): void {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.sendRequest('/projects/matches', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                if (data.matches.length > 0) {
                    Swordfish.mainWindow.webContents.send('set-matches', { project: arg.project, matches: data.matches });
                }
                Swordfish.mainWindow.webContents.send('end-waiting');
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getTerms(arg: any): void {
        if (!arg.glossary) {
            Swordfish.showMessage({ type: 'error', message: '未指定术语库，请先选择术语库！' });
            return;
        }
        let glossaryInfo = arg.glossary ? `[术语库ID]: ${arg.glossary}` : '[术语库ID]: 未指定';
        console.log('[术语匹配] 请求参数:', JSON.stringify(arg), glossaryInfo);
        Swordfish.sendRequest('/projects/terms', arg,
            (data: any) => {
                console.log('[术语匹配] 后端返回:', JSON.stringify(data), glossaryInfo);
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                let matchedTerms: any[] = [];
                if (data.terms && data.terms.length > 0 && arg.source) {
                    let src = (arg.source || '').trim();
                    matchedTerms = data.terms.filter((term: any) => {
                        let termSource = (term.source || '').trim();
                        // 只用 includes 匹配
                        return src.includes(termSource);
                    });
                }
                // 如果没有匹配到，返回全部术语
                if (!matchedTerms || matchedTerms.length === 0) {
                    matchedTerms = data.terms;
                }
                Swordfish.mainWindow.webContents.send('set-terms', { project: arg.project, terms: matchedTerms });
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
                console.log('[术语匹配] 请求失败:', reason, glossaryInfo);
            }
        );
    }

    static fixMatches(arg: any, srcLang: string, tgtLang: string): void {
        let mtManager: MTManager = new MTManager(Swordfish.currentPreferences, srcLang, tgtLang);
        try {
            mtManager.fixMatch(arg.matchData);
        } catch (error: any) {
            Swordfish.mainWindow.webContents.send('end-waiting');
            Swordfish.mainWindow.webContents.send('set-status', '');
            if (error instanceof Error) {
                Swordfish.showMessage({ type: 'error', message: error.message });
            } else {
                console.error(JSON.stringify(error));
            }
        }
    }

    static machineTranslate(arg: any): void {
        let mtManager: MTManager = new MTManager(Swordfish.currentPreferences, arg.srcLang, arg.tgtLang);
        try {
            mtManager.translateSegment(arg);
        } catch (error: any) {
            if (error instanceof Error) {
                Swordfish.showMessage({ type: 'error', message: error.message });
            } else {
                console.error(JSON.stringify(error));
            }
        }
    }

    static assembleMatches(arg: any): void {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Assembling Translations');
        Swordfish.sendRequest('/projects/assembleMatches', arg,
            (data: any) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                if (data.matches.length > 0) {
                    Swordfish.mainWindow.webContents.send('set-matches', { project: arg.project, matches: data.matches });
                }
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static tmTranslate(arg: any): void {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Searching Memory');
        Swordfish.sendRequest('/projects/tmTranslate', arg,
            (data: any) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                if (data.matches.length > 0) {
                    Swordfish.mainWindow.webContents.send('set-matches', { project: arg.project, matches: data.matches });
                }
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static showApplyTm(arg: any): void {
        this.applyTmWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 450,
            height: 190,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        if (arg.memory) {
            Swordfish.memoryParam = arg.memory;
        }
        Swordfish.projectParam = arg.project;
        this.applyTmWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'applyTm.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.applyTmWindow.loadURL(fileUrl.href);
        this.applyTmWindow.once('ready-to-show', () => {
            this.applyTmWindow.show();
        });
        this.applyTmWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.applyTmWindow, 'applyTm.html');
    }

    static tmTranslateAll(arg: any): void {
        Swordfish.applyTmWindow.close();
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Translating Project \u00A0\u00A0\u00A0 0%');
        Swordfish.sendRequest('/projects/tmTranslateAll', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.mainWindow.webContents.send('end-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', '');
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
                Swordfish.currentStatus = data;
                let processId: string = data.process;
                let percentage: number = 0;
                let intervalObject = setInterval(() => {
                    if (Swordfish.currentStatus.progress) {
                        if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            clearInterval(intervalObject);
                            Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                            Swordfish.showMessage({ type: 'info', message: 'Added translations to ' + Swordfish.currentStatus.translated + ' segments' });
                            return;
                        } else if (Swordfish.currentStatus.progress === Swordfish.PROCESSING) {
                            // it's OK, keep waiting
                            if (percentage !== Swordfish.currentStatus.percentage) {
                                percentage = Swordfish.currentStatus.percentage;
                                Swordfish.mainWindow.webContents.send('set-status', 'Translating Project \u00A0\u00A0\u00A0' + percentage + '%');
                            }
                        } else if (Swordfish.currentStatus.progress === Swordfish.ERROR) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            clearInterval(intervalObject);
                            Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                            return;
                        } else {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            clearInterval(intervalObject);
                            Swordfish.showMessage({ type: 'error', message: 'Unknown error applying TM' });
                            return;
                        }
                    }
                    Swordfish.getProjectsProgress(processId);
                }, 500);

            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getProjectMemories(arg: any): void {
        Swordfish.sendRequest('/projects/projectMemories', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                data.project = arg.project;
                Swordfish.mainWindow.webContents.send('set-project-memories', data);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getProjectGlossaries(arg: any): void {
        Swordfish.sendRequest('/projects/projectGlossaries', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
            return;
        }
                data.project = arg.project;
                Swordfish.mainWindow.webContents.send('set-project-glossaries', data);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static setProjectMemory(arg: any): void {
        Swordfish.sendRequest('/projects/setMemory', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static setProjectGlossary(arg: any): void {
        Swordfish.sendRequest('/projects/setGlossary', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static showSpellCheckerLangs(): void {
        Swordfish.spellingLangsWindow = new BrowserWindow({
            parent: this.preferencesWindow,
            width: 790,
            height: 530,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        Swordfish.spellingLangsWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'spellingLangs.html');
        let fileUrl: URL = new URL('file://' + filePath);
        Swordfish.spellingLangsWindow.loadURL(fileUrl.href);
        Swordfish.spellingLangsWindow.once('ready-to-show', () => {
            Swordfish.spellingLangsWindow.show();
        });
        this.spellingLangsWindow.on('close', () => {
            this.preferencesWindow.focus();
        });
        Swordfish.setLocation(this.spellingLangsWindow, 'spellingLangs.html');
    }

    static getSpellCheckerLangs(event: IpcMainEvent): void {
        Swordfish.sendRequest('/services/getSpellingLanguages', { languages: Swordfish.spellCheckerLanguages },
            (data: any) => {
                event.sender.send('set-spellchecker-langs', data);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static setSpellcheckerLanguage(lang: string): void {
        if (!Swordfish.spellCheckerLanguages) {
            Swordfish.spellCheckerLanguages = Swordfish.mainWindow.webContents.session.availableSpellCheckerLanguages;
        }
        if (Swordfish.spellCheckerLanguages.includes(lang)) {
            Swordfish.mainWindow.webContents.session.setSpellCheckerLanguages([lang]);
            return;
        }
        if (lang.startsWith('en')) {
            Swordfish.mainWindow.webContents.session.setSpellCheckerLanguages([Swordfish.currentPreferences.spellchecker.defaultEnglish]);
            return;
        }
        if (lang.startsWith('pt')) {
            Swordfish.mainWindow.webContents.session.setSpellCheckerLanguages([Swordfish.currentPreferences.spellchecker.defaultPortuguese]);
            return;
        }
        if (lang.startsWith('es')) {
            Swordfish.mainWindow.webContents.session.setSpellCheckerLanguages([Swordfish.currentPreferences.spellchecker.defaultSpanish]);
            return;
        }
        if (lang.length > 2) {
            lang = lang.substring(0, 2);
            if (Swordfish.spellCheckerLanguages.includes(lang)) {
                Swordfish.mainWindow.webContents.session.setSpellCheckerLanguages([lang]);
            }
        }
    }

    static showMessage(arg: any): void {
        let parent: BrowserWindow = Swordfish.mainWindow;
        if (arg.parent) {
            switch (arg.parent) {
                case 'goTo': parent = Swordfish.goToWindow;
                    break;
                case 'addFile': parent = Swordfish.addFileWindow;
                    break;
                case 'addGlossary': parent = Swordfish.addGlossaryWindow;
                    break;
                case 'addMemory': parent = Swordfish.addMemoryWindow;
                    break;
                case 'addProject': parent = Swordfish.addProjectWindow;
                    break;
                case 'addTerm': parent = Swordfish.addTermWindow;
                    break;
                case 'concordanceSearch': parent = Swordfish.concordanceSearchWindow;
                    break;
                case 'filterSegments': parent = Swordfish.filterSegmentsWindow;
                    break;
                case 'importGlossary': parent = Swordfish.importGlossaryWindow;
                    break;
                case 'importTmx': parent = Swordfish.importTmxWindow;
                    break;
                case 'importXliff': parent = Swordfish.importXliffWindow;
                    break;
                case 'preferences': parent = Swordfish.preferencesWindow;
                    break;
                case 'replaceText': parent = Swordfish.replaceTextWindow;
                    break;
                case 'termSearch': parent = Swordfish.termSearchWindow;
                    break;
                case 'applyTm': parent = Swordfish.applyTmWindow;
                    break;
                case 'addNote': parent = Swordfish.addNoteWindow;
                    break;
                case 'serverSettings': parent = Swordfish.serverSettingsWindow;
                    break;
                case 'browseDatabases': parent = Swordfish.browseDatabasesWindow;
                    break;
                case 'addConfiguration': parent = Swordfish.addXmlConfigurationWindow;
                    break;
                case 'filterConfig': parent = Swordfish.editXmlFilterWindow;
                    break;
                case 'elementConfig': parent = Swordfish.configElementWindow;
                    break;
                case 'tagsAnalysis': parent = Swordfish.tagsAnalysisWindow;
                    break;
                case 'spaceAnalysis': parent = Swordfish.spaceAnalysisWindow;
                    break;
                default: parent = Swordfish.mainWindow;
            }
        }
        dialog.showMessageBoxSync(parent, {
            icon: this.iconPath,
            type: arg.type,
            message: arg.message,
            buttons: ['OK']
        });
    }

    static showNotification(message: string): void {
        let notification: Notification = new Notification({
            title: message,
            silent: true,
            icon: this.iconPath
        });
        notification.show();
    }

    static importReviewedXLIFF(): void {
        dialog.showOpenDialog({
            title: 'Import XLIFF File',
            properties: ['openFile'],
            filters: [
                { name: 'XLIFF File', extensions: ['xlf'] },
                { name: 'Any File', extensions: ['*'] }
            ]
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                this.sendRequest('/projects/importReview', { xliff: value.filePaths[0] },
                    (result: any) => {
                        if (result.status === Swordfish.SUCCESS) {
                            Swordfish.showMessage({ type: 'info', message: 'XLIFF imported' });
                            // TODO refresh project if it is open
                        } else {
                            Swordfish.showMessage({ type: 'error', message: result.reason });
                        }
                    }, (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason });
                    });
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    static exportXLIFF(arg: any): void {
        let description = arg.description;
        if (description.lastIndexOf('/') !== -1) {
            description = description.substring(description.lastIndexOf('/'));
        }
        if (description.lastIndexOf('\\') !== -1) {
            description = description.substring(description.lastIndexOf('\\'));
        }
        dialog.showSaveDialog(Swordfish.mainWindow, {
            defaultPath: description + '.xlf',
            filters: [{ name: 'XLIFF Files', extensions: ['xlf'] }, { name: 'Any File', extensions: ['*'] }],
            properties: ['createDirectory', 'showOverwriteConfirmation']
        }).then((value: Electron.SaveDialogReturnValue) => {
            if (!value.canceled) {
                Swordfish.sendRequest('/projects/exportReview', { project: arg.projectId, output: value.filePath },
                    (data: any) => {
                        if (data.status === Swordfish.SUCCESS) {
                            Swordfish.exportProjectFile(data, 'Exporting XLIFF...', 'XLIFF exported');
                        } else {
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                        }
                    }, (reason: string) => {
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    static exportProject(arg: any): void {
        let description = arg.description;
        if (description.lastIndexOf('/') !== -1) {
            description = description.substring(description.lastIndexOf('/'));
        }
        if (description.lastIndexOf('\\') !== -1) {
            description = description.substring(description.lastIndexOf('\\'));
        }
        dialog.showSaveDialog(Swordfish.mainWindow, {
            defaultPath: description + '.xlf',
            filters: [{ name: 'XLIFF Files', extensions: ['xlf'] }, { name: 'Any File', extensions: ['*'] }],
            properties: ['createDirectory', 'showOverwriteConfirmation']
        }).then((value: Electron.SaveDialogReturnValue) => {
            if (!value.canceled) {
                Swordfish.sendRequest('/projects/export', { project: arg.projectId, output: value.filePath },
                    (data: any) => {
                        if (data.status === Swordfish.SUCCESS) {
                            Swordfish.exportProjectFile(data, 'Exporting project...', 'Project exported');
                        } else {
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                        }
                    }, (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    static exportProjectTMX(arg: any): void {
        let description = arg.description;
        if (description.lastIndexOf('/') !== -1) {
            description = description.substring(description.lastIndexOf('/'));
        }
        if (description.lastIndexOf('\\') !== -1) {
            description = description.substring(description.lastIndexOf('\\'));
        }
        dialog.showSaveDialog(Swordfish.mainWindow, {
            defaultPath: description + '.tmx',
            filters: [{ name: 'TMX Files', extensions: ['tmx'] }, { name: 'Any File', extensions: ['*'] }],
            properties: ['createDirectory', 'showOverwriteConfirmation']
        }).then((value: Electron.SaveDialogReturnValue) => {
            if (!value.canceled) {
                Swordfish.sendRequest('/projects/exportTmx', { project: arg.projectId, output: value.filePath },
                    (data: any) => {
                        Swordfish.exportProjectFile(data, 'Exporting TMX...', 'Translations exported');
                    }, (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    static exportMatches(arg: any): void {
        let description = arg.description;
        if (description.lastIndexOf('/') !== -1) {
            description = description.substring(description.lastIndexOf('/'));
        }
        if (description.lastIndexOf('\\') !== -1) {
            description = description.substring(description.lastIndexOf('\\'));
        }
        dialog.showSaveDialog(Swordfish.mainWindow, {
            defaultPath: description + '.tmx',
            filters: [{ name: 'TMX Files', extensions: ['tmx'] }, { name: 'Any File', extensions: ['*'] }],
            properties: ['createDirectory', 'showOverwriteConfirmation']
        }).then((value: Electron.SaveDialogReturnValue) => {
            if (!value.canceled) {
                Swordfish.sendRequest('/projects/exportMatches', { project: arg.projectId, output: value.filePath },
                    (data: any) => {
                        Swordfish.exportProjectFile(data, 'Exporting TM Matches...', 'TM matches exported');
                    }, (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    static exportTerms(arg: any): void {
        let description = arg.description;
        if (description.lastIndexOf('/') !== -1) {
            description = description.substring(description.lastIndexOf('/'));
        }
        if (description.lastIndexOf('\\') !== -1) {
            description = description.substring(description.lastIndexOf('\\'));
        }
        dialog.showSaveDialog(Swordfish.mainWindow, {
            defaultPath: description + '.tbx',
            filters: [{ name: 'TBX Files', extensions: ['tbx'] }, { name: 'Any File', extensions: ['*'] }],
            properties: ['createDirectory', 'showOverwriteConfirmation']
        }).then((value: Electron.SaveDialogReturnValue) => {
            if (!value.canceled) {
                Swordfish.sendRequest('/projects/exportTerms', { project: arg.projectId, output: value.filePath },
                    (data: any) => {
                        Swordfish.exportProjectFile(data, 'Exporting Terms...', 'Terms exported');
                    }, (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    static exportProjectFile(data: any, message: string, completed: string): void {
        if (data.status !== Swordfish.SUCCESS) {
            Swordfish.showMessage({ type: 'error', message: data.reason });
        }
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', message);
        Swordfish.currentStatus = data;
        let processId: string = data.process;
        let intervalObject = setInterval(() => {
            if (Swordfish.currentStatus.progress) {
                if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                    Swordfish.mainWindow.webContents.send('end-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', '');
                    clearInterval(intervalObject);
                    Swordfish.showMessage({ type: 'info', message: completed });
                    return;
                } else if (Swordfish.currentStatus.progress === Swordfish.PROCESSING) {
                    // it's OK, keep waiting
                } else if (Swordfish.currentStatus.progress === Swordfish.ERROR) {
                    Swordfish.mainWindow.webContents.send('end-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', '');
                    clearInterval(intervalObject);
                    Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                    return;
                } else {
                    Swordfish.mainWindow.webContents.send('end-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', '');
                    clearInterval(intervalObject);
                    Swordfish.showMessage({ type: 'error', message: 'Unknown error exporting project' });
                    return;
                }
            }
            Swordfish.getProjectsProgress(processId);
        }, 500);
    }

    static showAddGlossary(): void {
        this.addGlossaryWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 435,
            height: 290,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.addGlossaryWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'addGlossary.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.addGlossaryWindow.loadURL(fileUrl.href);
        this.addGlossaryWindow.once('ready-to-show', () => {
            this.addGlossaryWindow.show();
        });
        this.addGlossaryWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.addGlossaryWindow, 'addGlossary.html');
    }

    static removeGlossary(): void {
        Swordfish.mainWindow.webContents.send('remove-glossary');
    }

    static showImportXliff(): void {
        this.importXliffWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 580,
            height: 360,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.importXliffWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'importXliff.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.importXliffWindow.loadURL(fileUrl.href);
        this.importXliffWindow.once('ready-to-show', () => {
            this.importXliffWindow.show();
        });
        this.importXliffWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.importXliffWindow, 'importXliff.html');
    }

    static browseXLIFF(event: IpcMainEvent): void {
        dialog.showOpenDialog({
            title: 'Import Project File',
            properties: ['openFile'],
            filters: [
                { name: 'XLIFF File', extensions: ['xlf'] },
                { name: 'Any File', extensions: ['*'] }
            ]
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                event.sender.send('set-xliff', value.filePaths[0]);
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    static importXLIFF(arg: any): void {
        Swordfish.importXliffWindow.close();
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Importing XLIFF');
        Swordfish.sendRequest('/projects/import', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.mainWindow.webContents.send('end-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', '');
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
                Swordfish.currentStatus = data;
                let processId: string = data.process;
                let intervalObject = setInterval(() => {
                    if (Swordfish.currentStatus.progress) {
                        if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            clearInterval(intervalObject);
                            Swordfish.mainWindow.webContents.send('request-projects', { open: processId });
                            return;
                        } else if (Swordfish.currentStatus.progress === Swordfish.PROCESSING) {
                            // it's OK, keep waiting
                        } else if (Swordfish.currentStatus.progress === Swordfish.ERROR) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            clearInterval(intervalObject);
                            Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                            return;
                        } else {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            clearInterval(intervalObject);
                            Swordfish.showMessage({ type: 'error', message: 'Unknown error importing file' });
            return;
        }
                    }
                    Swordfish.getProjectsProgress(processId);
                }, 500);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static filesDropped(files: string[]): void {
        if (files.length === 1 && !(existsSync(files[0]) && lstatSync(files[0]).isDirectory())) {
            // single file
            Swordfish.selectedFiles = files;
            this.addFileWindow = new BrowserWindow({
                parent: this.mainWindow,
                width: 900,
                height: 355,
                minimizable: false,
                maximizable: false,
                resizable: false,
                show: false,
                icon: this.iconPath,
                webPreferences: {
                    nodeIntegration: true,
                    contextIsolation: false
                }
            });
            this.addFileWindow.setMenu(null);
            let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'addFile.html');
            let fileUrl: URL = new URL('file://' + filePath);
            this.addFileWindow.loadURL(fileUrl.href);
            this.addFileWindow.once('ready-to-show', () => {
                this.addFileWindow.show();
            });
            this.addFileWindow.on('close', () => {
                this.mainWindow.focus();
            });
            Swordfish.setLocation(this.addFileWindow, 'addFile.html');
        } else {
            let filesList: string[] = [];
            files.forEach((file: string) => {
                if (existsSync(file)) {
                    if (lstatSync(file).isDirectory()) {
                        let recursed: string[] = Swordfish.recurseFolder(file);
                        recursed.forEach((recursedFile: string) => {
                            filesList.push(recursedFile);
                        });
                    } else {
                        filesList.push(file);
                    }
                }
            });
            Swordfish.selectedFiles = filesList;
            Swordfish.showAddProject();
        }
    }

    static recurseFolder(file: string): string[] {
        let filesList: string[] = [];
        let dirFiles: string[] = readdirSync(file);
        dirFiles.forEach((dirFile: string) => {
            let child: string = this.path.join(file, dirFile)
            if (lstatSync(child).isDirectory()) {
                let recursed: string[] = Swordfish.recurseFolder(child);
                recursed.forEach((recursedFile: string) => {
                    filesList.push(recursedFile);
                });
            } else {
                filesList.push(child);
            }
        });
        return filesList;
    }

    static sortOptions(arg: any): void {
        Swordfish.mainWindow.webContents.send('set-sorting', arg);
        Swordfish.sortSegmentsWindow.close();
    }

    static filterOptions(arg: any): void {
        Swordfish.mainWindow.webContents.send('set-filters', arg);
        Swordfish.filterSegmentsWindow.close();
    }

    static removeTranslations(arg: any): void {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Remove all translations?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Removing translations');
                Swordfish.sendRequest('/projects/removeTranslations', arg,
            (data: any) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                            return;
                        }
                        Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                        Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics });
            },
            (reason: string) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static removeAssembledMatches(arg: any): void {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Remove all auto-translations?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Removing auto-translations');
                Swordfish.sendRequest('/projects/removeAssembledMatches', arg,
            (data: any) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                            return;
                        }
                        Swordfish.mainWindow.webContents.send('reload-page', arg.project);
            },
            (reason: string) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static removeMatches(arg: any): void {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Remove all translation memory matches?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Removing matches');
                Swordfish.sendRequest('/projects/removeMatches', arg,
                    (data: any) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.showMessage({ type: 'error', message: data.reason });
            return;
        }
                        Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                    },
                    (reason: string) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static removeMachineTranslations(arg: any): void {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Remove all machine translations?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Removing translations');
                Swordfish.sendRequest('/projects/removeMT', arg,
            (data: any) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                            return;
                        }
                        Swordfish.mainWindow.webContents.send('reload-page', arg.project);
            },
            (reason: string) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static unconfirmTranslations(arg: any): void {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Unconfirm all translations?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Updating status');
                Swordfish.sendRequest('/projects/unconfirmTranslations', arg,
            (data: any) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                            return;
                        }
                        Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                        Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics });
            },
            (reason: string) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static pseudoTranslate(arg: any): void {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Pseudo-translate untranslated segments?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Pseudo-translating');
                Swordfish.sendRequest('/projects/pseudoTranslate', arg,
                    (data: any) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                            return;
                        }
                        Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                        Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics });
                    },
                    (reason: string) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static copyAllSources(arg: any): void {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Copy source to all empty targets?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Copying sources');
                Swordfish.sendRequest('/projects/copyAllSources', arg,
                    (data: any) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.showMessage({ type: 'error', message: data.reason });
            return;
        }
                        Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                        Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics });
                    },
                    (reason: string) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static confirmAllTranslations(arg: any): void {

        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Confirm all translations?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Confirming translations');
                Swordfish.sendRequest('/projects/confirmAllTranslations', arg,
                    (data: any) => {
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                        }
                        Swordfish.currentStatus = data;
                        let processId: string = data.process;
                        let intervalObject = setInterval(() => {
                            if (Swordfish.currentStatus.progress) {
                                if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                                    Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics });
                                    return;
                                } else if (Swordfish.currentStatus.progress === Swordfish.PROCESSING) {
                                    // it's OK, keep waiting
                                } else if (Swordfish.currentStatus.progress === Swordfish.ERROR) {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                                    return;
                                } else {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.showMessage({ type: 'error', message: 'Unknown error confirming translations' });
            return;
        }
                            }
                            Swordfish.getProjectsProgress(processId);
                        }, 500);
                    },
                    (reason: string) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static acceptAll100Matches(arg: any): void {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Accept all 100% matches?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Accepting matches');
                Swordfish.sendRequest('/projects/acceptAll100Matches', arg,
                    (data: any) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.showMessage({ type: 'error', message: data.reason });
            return;
        }
                        Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                        Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics });
                    },
                    (reason: string) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static generateStatistics(arg: any) {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Generating statistics');
        Swordfish.sendRequest('/projects/generateStatistics', arg,
            (data: any) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                shell.openExternal('file://' + data.analysis).catch(() => {
                    shell.openPath(data.analysis).catch((reason: any) => {
                        if (reason instanceof Error) {
                            console.error(reason.message);
                        }
                        this.showMessage({ type: 'error', message: 'Unable to open statistics.' });
                    });
                });
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static showTagsWindow(): void {
        this.tagsWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 190,
            height: 150,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.tagsWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'tags.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.tagsWindow.loadURL(fileUrl.href);
        this.tagsWindow.once('ready-to-show', () => {
            this.tagsWindow.show();
        });
        this.tagsWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.tagsWindow, 'tags.html');
    }

    static showDoubaoSettings(): void {
        this.doubaoSettingsWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 650,
            height: 800,
            minimizable: false,
            maximizable: false,
            resizable: true,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.doubaoSettingsWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'doubaoSettings.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.doubaoSettingsWindow.loadURL(fileUrl.href);
        this.doubaoSettingsWindow.once('ready-to-show', () => {
            this.doubaoSettingsWindow.show();
        });
        this.doubaoSettingsWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.doubaoSettingsWindow, 'doubaoSettings.html');
    }

    static showGoToWindow(): void {
        this.goToWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 250,
            height: 150,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.goToWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'goTo.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.goToWindow.loadURL(fileUrl.href);
        this.goToWindow.once('ready-to-show', () => {
            this.goToWindow.show();
        });
        this.goToWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.goToWindow, 'goTo.html');
    }

    static closeTagsWindow(): void {
        if (this.tagsWindow?.isVisible()) {
            Swordfish.tagsWindow.close();
        }
    }

    static showReplaceText(arg: any): void {
        this.replaceTextWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 450,
            height: 265,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.projectParam = arg.project;
        this.replaceTextWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'replaceText.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.replaceTextWindow.loadURL(fileUrl.href);
        this.replaceTextWindow.once('ready-to-show', () => {
            this.replaceTextWindow.show();
        });
        this.replaceTextWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.replaceTextWindow, 'replaceText.html');
    }

    static replaceText(arg: any): void {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Replacing text');
        Swordfish.sendRequest('/projects/replaceText', arg,
            (data: any) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                Swordfish.replaceTextWindow.close();
                Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics });
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static assembleMatchesAll(arg: any): void {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Apply Auto-Translation to all segments?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Assembling Matches');
                Swordfish.sendRequest('/projects/applyAmAll', arg,
            (data: any) => {
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                        }
                        Swordfish.currentStatus = data;
                        let processId: string = data.process;
                        let intervalObject = setInterval(() => {
                            if (Swordfish.currentStatus.progress) {
                                if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                                    return;
                                } else if (Swordfish.currentStatus.progress === Swordfish.PROCESSING) {
                                    // it's OK, keep waiting
                                } else if (Swordfish.currentStatus.progress === Swordfish.ERROR) {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                                    return;
                                } else {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.showMessage({ type: 'error', message: 'Unknown error auto-translating' });
                                    return;
                                }
                            }
                            Swordfish.getProjectsProgress(processId);
                        }, 500);
            },
            (reason: string) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static applyMachineTranslationsAll(arg: any): void {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Apply Machine Translation to all segments?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Selecting segments...');
                Swordfish.sendRequest('/projects/applyMtAll', arg,
            (data: any) => {
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                        }
                        Swordfish.currentStatus = data;
                        let processId: string = data.process;
                        let intervalObject = setInterval(() => {
                            if (Swordfish.currentStatus.progress) {
                                if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                                    clearInterval(intervalObject);
                                    Swordfish.mainWindow.webContents.send('set-status', 'Translating...');
                                    let exportedFile: string = Swordfish.path.join(Swordfish.currentPreferences.projectsFolder, arg.project, 'applymt.xlf');
                                    if (!existsSync(exportedFile)) {
                                        Swordfish.mainWindow.webContents.send('end-waiting');
                                        Swordfish.mainWindow.webContents.send('set-status', '');
                                        Swordfish.showMessage({ type: 'error', message: 'Unable to find exported file' });
                                        return;
                                    }
                                    try {
                                        let mtManager: MTManager = new MTManager(this.currentPreferences, arg.srcLang, arg.tgtLang);
                                        mtManager.translateProject(arg.project, exportedFile, arg.currentSegment);
                                        unlinkSync(exportedFile);
                                        Swordfish.mainWindow.webContents.send('end-waiting');
                                        Swordfish.mainWindow.webContents.send('set-status', '');
                                        Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                                    } catch (e) {
                                        Swordfish.mainWindow.webContents.send('end-waiting');
                                        Swordfish.mainWindow.webContents.send('set-status', '');
                                        if (e instanceof Error) {
                                            Swordfish.showMessage({ type: 'error', message: e.message });
                                        } else {
                                            Swordfish.showMessage({ type: 'error', message: 'Unknown error applying MT' });
                                            console.error(e);
                                        }
                                    }
                                    return;
                                } else if (Swordfish.currentStatus.progress === Swordfish.PROCESSING) {
                                    // it's OK, keep waiting
                                } else if (Swordfish.currentStatus.progress === Swordfish.ERROR) {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                                    return;
                                } else {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.showMessage({ type: 'error', message: 'Unknown error applying MT' });
                                    return;
                                }
                            }
                            Swordfish.getProjectsProgress(processId);
                        }, 500);
            },
            (reason: string) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static acceptAllMachineTranslations(arg: any) {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Accept all machine translations?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Accepting matches');
                Swordfish.sendRequest('/projects/acceptAllMT', arg,
            (data: any) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                            return;
                        }
                        Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                        Swordfish.mainWindow.webContents.send('set-statistics', { project: arg.project, statistics: data.statistics });
            },
            (reason: string) => {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static importGlossaryFile(arg: any): void {
        Swordfish.importGlossaryWindow.close();
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Importing glossary');
        Swordfish.sendRequest('/glossaries/import', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.mainWindow.webContents.send('end-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', '');
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
                Swordfish.currentStatus = data;
                let processId: string = data.process;
                let intervalObject = setInterval(() => {
                    if (Swordfish.currentStatus.status === Swordfish.SUCCESS) {
                        if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            clearInterval(intervalObject);
                            Swordfish.showMessage({ type: 'info', message: 'Imported ' + Swordfish.currentStatus.imported + ' terms.' });
                            return;
                        }
                    }
                    if (Swordfish.currentStatus.status === Swordfish.ERROR) {
                        Swordfish.mainWindow.webContents.send('end-waiting');
                        Swordfish.mainWindow.webContents.send('set-status', '');
                        clearInterval(intervalObject);
                        Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                        return;
                    }
                    Swordfish.getGlossariesProgress(processId);
                }, 500);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static showConcordanceWindow(memories: string[]): void {
        this.concordanceSearchWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 470,
            height: 300,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        Swordfish.concordanceMemories = memories;
        this.concordanceSearchWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'concordanceSearch.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.concordanceSearchWindow.loadURL(fileUrl.href);
        this.concordanceSearchWindow.once('ready-to-show', () => {
            this.concordanceSearchWindow.show();
        });
        this.concordanceSearchWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.concordanceSearchWindow, 'concordanceSearch.html');
    }

    static showGlossaryManager(glossaryId: string, glossaryName: string): void {
        this.glossaryManagerWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 900,
            height: 600,
            minimizable: false,
            maximizable: true,
            resizable: true,
            show: false,
            icon: this.iconPath,
            titleBarStyle: process.platform === 'darwin' ? 'default' : undefined,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.glossaryManagerWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'glossaryManager.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.glossaryManagerWindow.loadURL(fileUrl.href);
        this.glossaryManagerWindow.once('ready-to-show', () => {
            this.glossaryManagerWindow.show();
        });
        this.glossaryManagerWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.glossaryManagerWindow, 'glossaryManager.html');
        
        // 传递术语库信息
        this.glossaryManagerWindow.webContents.once('did-finish-load', () => {
            this.glossaryManagerWindow.webContents.send('set-glossary-info', {
                glossaryId: glossaryId,
                glossaryName: glossaryName
            });
        });
    }

    static showAIAssistant(): void {
        // 如果窗口已存在且未销毁，则显示并聚焦
        if (this.aiAssistantWindow && !this.aiAssistantWindow.isDestroyed()) {
            this.aiAssistantWindow.show();
            this.aiAssistantWindow.focus();
            return;
        }

        // 创建AI助手悬浮窗口
        this.aiAssistantWindow = new BrowserWindow({
            width: 400,
            height: 500,
            minWidth: 350,
            minHeight: 300,
            x: 100, // 靠近右下角
            y: 100,
            frame: false, // 无边框，使用自定义标题栏
            alwaysOnTop: true, // 始终置顶
            skipTaskbar: false,
            resizable: true,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false,
                webSecurity: false
            }
        });

        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'aiAssistant.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.aiAssistantWindow.loadURL(fileUrl.href);
        
        this.aiAssistantWindow.once('ready-to-show', () => {
            this.aiAssistantWindow.show();
        });

        this.aiAssistantWindow.on('close', () => {
            // 窗口关闭时清理引用
            this.aiAssistantWindow = undefined as any;
        });

        // 设置窗口位置
        Swordfish.setLocation(this.aiAssistantWindow, 'aiAssistant.html');
        
        console.log('AI助手窗口已创建');
    }

    static handleAIChat(event: IpcMainEvent, data: any): void {
        console.log('收到AI聊天请求:', data);
        
        // 检查数据格式并构建消息数组
        let messages: any[];
        if (Array.isArray(data)) {
            // 直接是消息数组
            messages = data.map((msg: any) => ({
                role: msg.role,
                content: msg.content
            }));
        } else if (data.messages && Array.isArray(data.messages)) {
            // 包含messages属性的对象
            messages = data.messages.map((msg: any) => ({
                role: msg.role,
                content: msg.content
            }));
        } else {
            console.error('无效的消息格式:', data);
            if (this.aiAssistantWindow && !this.aiAssistantWindow.isDestroyed()) {
                this.aiAssistantWindow.webContents.send('ai-error', '消息格式错误');
            }
            return;
        }

        // 获取当前AI设置
        const aiSettings = this.getAISettings();
        
        // 根据设置选择AI提供商
        if (aiSettings.provider === 'anythingllm' && aiSettings.anythingllm) {
            this.callAnythingLLMAPI(messages, aiSettings.anythingllm)
                .then((response: string) => {
                    if (this.aiAssistantWindow && !this.aiAssistantWindow.isDestroyed()) {
                        this.aiAssistantWindow.webContents.send('ai-response', response);
                    }
                })
                .catch((error: Error) => {
                    console.error('AnythingLLM API调用失败:', error);
                    if (this.aiAssistantWindow && !this.aiAssistantWindow.isDestroyed()) {
                        this.aiAssistantWindow.webContents.send('ai-error', error.message);
                    }
                });
        } else {
            // 默认使用DeepSeek
            this.callDeepSeekAPI(messages)
                .then((response: string) => {
                    if (this.aiAssistantWindow && !this.aiAssistantWindow.isDestroyed()) {
                        this.aiAssistantWindow.webContents.send('ai-response', response);
                    }
                })
                .catch((error: Error) => {
                    console.error('DeepSeek API调用失败:', error);
                    if (this.aiAssistantWindow && !this.aiAssistantWindow.isDestroyed()) {
                        this.aiAssistantWindow.webContents.send('ai-error', error.message);
                    }
                });
        }
    }

    static async callDeepSeekAPI(messages: any[]): Promise<string> {
        const https = require('https');
        
        // 从Machine Translation配置中获取DeepSeek API密钥
        const apiKey = this.currentPreferences.deepseek?.apiKey;
        
        if (!apiKey) {
            throw new Error('请在偏好设置 → Machine Translation → DeepSeek中配置API密钥');
        }

        const requestData = JSON.stringify({
            model: "deepseek-chat",
            messages: messages,
            stream: false,
            temperature: 0.7,
            max_tokens: 2000
        });

        const options = {
            hostname: 'api.deepseek.com',
            port: 443,
            path: '/chat/completions',
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${apiKey}`,
                'Content-Length': Buffer.byteLength(requestData)
            }
        };

        return new Promise((resolve, reject) => {
            const req = https.request(options, (res: any) => {
                let data = '';

                res.on('data', (chunk: any) => {
                    data += chunk;
                });

                res.on('end', () => {
                    try {
                        const response = JSON.parse(data);
                        
                        if (response.error) {
                            reject(new Error(response.error.message || 'API请求失败'));
                return;
            }

                        if (response.choices && response.choices[0] && response.choices[0].message) {
                            resolve(response.choices[0].message.content);
                        } else {
                            reject(new Error('API响应格式错误'));
                        }
                    } catch (error) {
                        reject(new Error('解析API响应失败: ' + (error as Error).message));
                    }
                });
            });

            req.on('error', (error: Error) => {
                reject(error);
            });

            req.write(requestData);
            req.end();
        });
    }

    static async callAnythingLLMAPI(messages: any[], config: any): Promise<string> {
        const axios = require('axios');
        
        // 获取最后一条用户消息作为问题
        const userMessages = messages.filter(msg => msg.role === 'user');
        const lastUserMessage = userMessages[userMessages.length - 1];
        
        if (!lastUserMessage) {
            throw new Error('未找到用户消息');
        }

        try {
            // 首先获取聊天历史，为新消息提供上下文
            console.log('获取AnythingLLM聊天历史...');
            const historyResponse = await axios.get(
                `${config.url}/api/v1/workspace/${config.selectedWorkspace.slug}/chats`,
                {
                    headers: {
                        'Authorization': `Bearer ${config.apiKey}`,
                        'Accept': 'application/json'
                    },
                    timeout: 10000,
                    proxy: false,
                    httpsAgent: undefined,
                    httpAgent: undefined
                }
            );

            // 发送聊天消息
            console.log('发送AnythingLLM聊天消息...');
            const response = await axios.post(
                `${config.url}/api/v1/workspace/${config.selectedWorkspace.slug}/chat`,
                {
                    message: lastUserMessage.content,
                    mode: 'chat'
                },
                {
                    headers: {
                        'Authorization': `Bearer ${config.apiKey}`,
                        'Content-Type': 'application/json'
                    },
                    timeout: 60000, // 增加超时时间到60秒
                    proxy: false,
                    httpsAgent: undefined,
                    httpAgent: undefined
                }
            );

            if (response.data && response.data.textResponse) {
                let result = response.data.textResponse;
                
                // 如果响应包含来源信息，添加到回复中
                if (response.data.sources && Array.isArray(response.data.sources) && response.data.sources.length > 0) {
                    result += '\n\n📚 **来源文档：**\n';
                    response.data.sources.forEach((source: any, index: number) => {
                        if (source.title) {
                            result += `${index + 1}. ${source.title}\n`;
                        } else if (source.source) {
                            result += `${index + 1}. ${source.source}\n`;
                        }
                    });
                }
                
                return result;
            } else if (response.data && response.data.message) {
                return response.data.message;
            } else {
                throw new Error('AnythingLLM API响应格式错误');
            }
        } catch (error: any) {
            console.error('AnythingLLM API调用详细错误:', error);
            
            if (error.code === 'ECONNABORTED') {
                throw new Error('AnythingLLM响应超时，请稍后重试或检查服务器状态');
            } else if (error.response?.status === 401) {
                throw new Error('API密钥无效，请检查您的AnythingLLM设置');
            } else if (error.response?.status === 404) {
                throw new Error('工作区未找到，请检查工作区配置');
            } else if (error.code === 'ECONNREFUSED') {
                throw new Error('无法连接到AnythingLLM服务器，请检查服务器地址');
            } else {
                throw new Error(error.response?.data?.message || error.message || 'AnythingLLM请求失败');
            }
        }
    }

    static concordanceSearch(event: IpcMainEvent, arg: any): void {
        event.sender.send('start-waiting');
        Swordfish.sendRequest('/memories/concordance', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    event.sender.send('end-waiting');
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                Swordfish.currentStatus = data;
                let processId: string = data.process;
                let intervalObject = setInterval(() => {
                    if (Swordfish.currentStatus.status === Swordfish.SUCCESS) {
                        if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                            clearInterval(intervalObject);
                            Swordfish.concordanceResults(Swordfish.currentStatus);
                            event.sender.send('end-waiting');
                            return;
                        }
                    }
                    if (Swordfish.currentStatus.status === Swordfish.ERROR) {
                        event.sender.send('end-waiting');
                        clearInterval(intervalObject);
                        Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                        return;
                    }
                    Swordfish.getMemoriesProgress(processId);
                }, 500);
            },
            (reason: string) => {
                event.sender.send('end-waiting');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static concordanceResults(data: any): void {
        if (data.count === 0) {
            Swordfish.showMessage({ type: 'info', message: 'Text not found' });
            return;
        }
        let size: Rectangle = Swordfish.mainWindow.getBounds();
        let htmlViewerWindow: BrowserWindow = new BrowserWindow({
            parent: Swordfish.concordanceSearchWindow,
            width: size.width * 0.6,
            height: size.height * 0.4,
            minimizable: false,
            maximizable: false,
            resizable: true,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        Swordfish.htmlContent = data.html;
        Swordfish.htmlTitle = 'Concordance Search';
        Swordfish.htmlId = htmlViewerWindow.id;
        htmlViewerWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'htmlViewer.html');
        let fileUrl: URL = new URL('file://' + filePath);
        htmlViewerWindow.loadURL(fileUrl.href);
        htmlViewerWindow.once('ready-to-show', () => {
            htmlViewerWindow.show();
        });
        htmlViewerWindow.on('close', () => {
            this.concordanceSearchWindow.focus();
        });
    }

    static showIatePlugin(): void {
        this.iatePluginWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 600,
            height: 510,
            minimizable: true,
            maximizable: false,
            resizable: true,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.iatePluginWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'iatePlugin.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.iatePluginWindow.loadURL(fileUrl.href);
        this.iatePluginWindow.once('ready-to-show', () => {
            this.iatePluginWindow.show();
        });
        this.iatePluginWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.iatePluginWindow, 'iatePlugin.html');
    }

    static showTermOnlinePlugin(): void {
        this.termOnlinePluginWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 1000,
            height: 700,
            minimizable: true,
            maximizable: true,
            resizable: true,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.termOnlinePluginWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'termOnlinePlugin.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.termOnlinePluginWindow.loadURL(fileUrl.href);
        this.termOnlinePluginWindow.once('ready-to-show', () => {
            this.termOnlinePluginWindow.show();
        });
        this.termOnlinePluginWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.termOnlinePluginWindow, 'termOnlinePlugin.html');
    }

    static showTermSearch(arg: any): any {
        this.termSearchWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 500,
            height: 280,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.glossaryParam = arg.glossary;
        this.termSearchWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'termSearch.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.termSearchWindow.loadURL(fileUrl.href);
        this.termSearchWindow.once('ready-to-show', () => {
            this.termSearchWindow.show();
        });
        this.termSearchWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.termSearchWindow, 'termSearch.html');
    }

    static termSearch(arg: any): void {
        Swordfish.sendRequest('/glossaries/search', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                if (data.count === 0) {
                    Swordfish.showMessage({ type: 'info', message: 'Term not found' });
                    return;
                }
                let size: Rectangle = Swordfish.mainWindow.getBounds();
                let htmlViewerWindow: BrowserWindow = new BrowserWindow({
                    parent: Swordfish.termSearchWindow,
                    width: size.width * 0.6,
                    height: size.height * 0.4,
                    minimizable: false,
                    maximizable: false,
                    resizable: true,
                    show: false,
                    icon: this.iconPath,
                    webPreferences: {
                        nodeIntegration: true,
                        contextIsolation: false,
                        webSecurity: false,
                        allowRunningInsecureContent: true
                    }
                });
                Swordfish.htmlTitle = 'Term Search';
                Swordfish.htmlContent = data.html;
                Swordfish.htmlId = htmlViewerWindow.id;
                htmlViewerWindow.setMenu(null);
                let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'htmlViewer.html');
                let fileUrl: URL = new URL('file://' + filePath);
                htmlViewerWindow.loadURL(fileUrl.href);
                htmlViewerWindow.once('ready-to-show', () => {
                    htmlViewerWindow.show();
                });
                htmlViewerWindow.on('close', () => {
                    this.termSearchWindow.focus();
                });

            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static showAddTerm(glossary: string) {
        this.addTermWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 680,
            height: 190,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.glossaryParam = glossary;
        this.addTermWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'addTerm.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.addTermWindow.loadURL(fileUrl.href);
        this.addTermWindow.once('ready-to-show', () => {
            this.addTermWindow.show();
        });
        this.addTermWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.addTermWindow, 'addTerm.html');
    }

    static showEditTerm(glossary: string, termId: string, term: any) {
        this.editTermWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 500,
            height: 300,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.editTermWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'editTerm.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.editTermWindow.loadURL(fileUrl.href);
        this.editTermWindow.once('ready-to-show', () => {
            this.editTermWindow!.show();
        });
        this.editTermWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.editTermWindow, 'editTerm.html');
        
        // 传递编辑参数
        this.editTermWindow.webContents.once('did-finish-load', () => {
            console.log('发送术语编辑参数:', {
                glossary: glossary,
                termId: termId,
                term: term
            });
            this.editTermWindow!.webContents.send('set-edit-term-params', {
                glossary: glossary,
                termId: termId,
                term: term
            });
        });
    }

    static addToGlossary(arg: any): void {
        Swordfish.sendRequest('/glossaries/addTerm', arg,
            (data: any) => {
                let isSuccess = false;
                let message = '';
                if (data && typeof data === 'object' && data.status === Swordfish.SUCCESS) {
                    isSuccess = true;
                    message = '术语添加成功';
                } else if (data && typeof data === 'object' && data.reason) {
                    message = data.reason;
                } else if (data && typeof data === 'string') {
                    message = data;
                } else {
                    message = '添加术语失败，未知错误';
                }
                Swordfish.addTermWindow.webContents.send('add-to-glossary-result', { success: isSuccess, message });
                setTimeout(() => {
                    if (Swordfish.addTermWindow) {
                        Swordfish.addTermWindow.close();
                    }
                }, 800);
            },
            (reason: string) => {
                Swordfish.addTermWindow.webContents.send('add-to-glossary-result', { success: false, message: reason });
                setTimeout(() => {
                    if (Swordfish.addTermWindow) {
                        Swordfish.addTermWindow.close();
                    }
                }, 800);
            }
        );
    }

    static updateTerm(arg: any): void {
        Swordfish.sendRequest('/glossaries/updateTerm', arg,
            (data: any) => {
                let isSuccess = false;
                let message = '';
                if (data && typeof data === 'object' && data.status === Swordfish.SUCCESS) {
                    isSuccess = true;
                    message = '术语更新成功';
                } else if (data && typeof data === 'object' && data.reason) {
                    message = data.reason;
                } else if (data && typeof data === 'string') {
                    message = data;
                } else {
                    message = '更新术语失败，未知错误';
                }
                Swordfish.showMessage({ type: isSuccess ? 'info' : 'error', message });
                if (Swordfish.editTermWindow && !Swordfish.editTermWindow.isDestroyed()) {
                    Swordfish.editTermWindow.close();
                }
                // 如果更新成功，刷新术语搜索结果
                if (isSuccess && Swordfish.termSearchWindow) {
                    console.log('更新成功，刷新术语搜索结果');
                    Swordfish.termSearchWindow.webContents.send('refresh-search-results');
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
                if (Swordfish.editTermWindow && !Swordfish.editTermWindow.isDestroyed()) {
                    Swordfish.editTermWindow.close();
                }
            }
        );
    }

    static deleteTerm(arg: any): void {
        Swordfish.sendRequest('/glossaries/deleteTerm', arg,
                (data: any) => {
                let isSuccess = false;
                let message = '';
                if (data && typeof data === 'object' && data.status === Swordfish.SUCCESS) {
                    isSuccess = true;
                    message = '术语删除成功';
                } else if (data && typeof data === 'object' && data.reason) {
                    message = data.reason;
                } else if (data && typeof data === 'string') {
                    message = data;
                } else {
                    message = '删除术语失败，未知错误';
                }
                
                // 先关闭编辑窗口，避免后续操作中的对象访问错误
                if (Swordfish.editTermWindow && !Swordfish.editTermWindow.isDestroyed()) {
                    Swordfish.editTermWindow.close();
                    Swordfish.editTermWindow = null;
                }
                
                // 显示消息
                Swordfish.showMessage({ type: isSuccess ? 'info' : 'error', message });
                
                // 如果删除成功，刷新相关窗口
                if (isSuccess) {
                    // 刷新术语搜索结果
                    if (Swordfish.termSearchWindow && !Swordfish.termSearchWindow.isDestroyed()) {
                        console.log('删除成功，刷新术语搜索结果');
                        setTimeout(() => {
                            if (Swordfish.termSearchWindow && !Swordfish.termSearchWindow.isDestroyed()) {
                                Swordfish.termSearchWindow.webContents.send('refresh-search-results');
                            }
                        }, 100);
                    }
                    
                    // 刷新术语库管理器
                    if (Swordfish.glossaryManagerWindow && !Swordfish.glossaryManagerWindow.isDestroyed()) {
                        console.log('删除成功，刷新术语库管理器');
                        setTimeout(() => {
                            if (Swordfish.glossaryManagerWindow && !Swordfish.glossaryManagerWindow.isDestroyed()) {
                                Swordfish.glossaryManagerWindow.webContents.send('refresh-terms');
                            }
                        }, 100);
                    }
                }
            },
            (reason: string) => {
                // 先关闭编辑窗口
                if (Swordfish.editTermWindow && !Swordfish.editTermWindow.isDestroyed()) {
                    Swordfish.editTermWindow.close();
                    Swordfish.editTermWindow = null;
                }
                // 显示错误消息
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getSegmentTerms(arg: any) {
        Swordfish.sendRequest('/projects/getSegmentTerms', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                if (data.terms.length > 0) {
                    Swordfish.mainWindow.webContents.send('set-terms', { project: arg.project, terms: data.terms });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getProjectTerms(arg: any) {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Get terms for all segments?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                Swordfish.mainWindow.webContents.send('start-waiting');
                Swordfish.mainWindow.webContents.send('set-status', 'Getting terms');
                Swordfish.sendRequest('/projects/getProjectTerms', arg,
                    (data: any) => {
                        if (data.status !== Swordfish.SUCCESS) {
                            Swordfish.mainWindow.webContents.send('end-waiting');
                            Swordfish.mainWindow.webContents.send('set-status', '');
                            Swordfish.showMessage({ type: 'error', message: data.reason });
                        }
                        Swordfish.currentStatus = data;
                        let processId: string = data.process;
                        let intervalObject = setInterval(() => {
                            if (Swordfish.currentStatus.progress) {
                                if (Swordfish.currentStatus.progress === Swordfish.COMPLETED) {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    if (Swordfish.currentStatus.segments > 0) {
                                        Swordfish.mainWindow.webContents.send('reload-page', arg.project);
                                        Swordfish.showMessage({ type: 'info', message: 'Added terms to ' + Swordfish.currentStatus.segments + ' segments' });
                                        return;
                                    }
                                    Swordfish.showMessage({ type: 'info', message: 'Terms not found' });
                                    return;
                                } else if (Swordfish.currentStatus.progress === Swordfish.PROCESSING) {
                                    // it's OK, keep waiting
                                } else if (Swordfish.currentStatus.progress === Swordfish.ERROR) {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.showMessage({ type: 'error', message: Swordfish.currentStatus.reason });
                                    return;
                    } else {
                                    Swordfish.mainWindow.webContents.send('end-waiting');
                                    Swordfish.mainWindow.webContents.send('set-status', '');
                                    clearInterval(intervalObject);
                                    Swordfish.showMessage({ type: 'error', message: 'Unknown error getting terms' });
                                    return;
                                }
                            }
                            Swordfish.getProjectsProgress(processId);
                        }, 500);
                },
                (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            }
        });
    }

    static lockSegment(arg: any) {
        Swordfish.sendRequest('/projects/lockSegment', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static lockDuplicates(arg: any) {
        Swordfish.sendRequest('/projects/lockDuplicates', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                Swordfish.mainWindow.webContents.send('reload-page', arg.project);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static unlockAll(projectId: string) {
        Swordfish.sendRequest('/projects/unlockAll', { project: projectId },
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                Swordfish.mainWindow.webContents.send('reload-page', projectId);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static analyzeSpaces(projectId: string): void {
        Swordfish.activeProject = projectId;
        Swordfish.spaceAnalysisWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 540,
            height: 350,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        Swordfish.spaceAnalysisWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'spaceAnalysis.html');
        let fileUrl: URL = new URL('file://' + filePath);
        Swordfish.spaceAnalysisWindow.loadURL(fileUrl.href);
        Swordfish.spaceAnalysisWindow.once('ready-to-show', () => {
            Swordfish.spaceAnalysisWindow.show();
        });
        Swordfish.spaceAnalysisWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(Swordfish.spaceAnalysisWindow, 'spaceAnalysis.html');
    }

    static analyzeTags(projectId: string): void {
        Swordfish.activeProject = projectId;
        Swordfish.tagsAnalysisWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 400,
            height: 350,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        Swordfish.tagsAnalysisWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'tagsAnalysis.html');
        let fileUrl: URL = new URL('file://' + filePath);
        Swordfish.tagsAnalysisWindow.loadURL(fileUrl.href);
        Swordfish.tagsAnalysisWindow.once('ready-to-show', () => {
            Swordfish.tagsAnalysisWindow.show();
        });
        Swordfish.tagsAnalysisWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(Swordfish.tagsAnalysisWindow, 'tagsAnalysis.html');
    }

    static getTagErrors(event: IpcMainEvent): void {
        Swordfish.sendRequest('/projects/analyzeTags', { project: Swordfish.activeProject },
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                event.sender.send('set-tagsErrors', data);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static getSpaceErrors(event: IpcMainEvent): void {
        Swordfish.sendRequest('/projects/analyzeSpaces', { project: Swordfish.activeProject },
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                event.sender.send('set-spaceErrors', data);
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static fixSpaceErrors(event: IpcMainEvent): void {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Fixing spaces');
        Swordfish.sendRequest('/projects/fixSpaces', { project: Swordfish.activeProject },
            (data: any) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                event.sender.send('set-spaceErrors', data);
                Swordfish.mainWindow.webContents.send('reload-page', Swordfish.activeProject);
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static openPrompt(args: any): void {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Generating Prompt');
        Swordfish.sendRequest('/projects/getSegment', args,
            (data: any) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                try {
                    if (data.status !== Swordfish.SUCCESS) {
                        console.error('Swordfish.generatePrompt', data);
                    } else {
                        let source: XMLElement = MTUtils.toXMLElement(data.source);
                        let prompt: string = MTUtils.getRole(data.srcLang, data.tgtLang) + ' ' + MTUtils.generatePrompt(source, data.srcLang, data.tgtLang, data.terms);
                        Swordfish.showPromptDialog(prompt);
                    }
                } catch (e) {
                    if (e instanceof Error) {
                        Swordfish.showMessage({ type: 'error', message: e.message });
                    }
                }
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static generatePrompt(args: any): void {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Generating Prompt');
        Swordfish.sendRequest('/projects/getSegment', args,
            (data: any) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                try {
                    if (data.status !== Swordfish.SUCCESS) {
                        console.error('Swordfish.generatePrompt', data);
                    } else {
                        let source: XMLElement = MTUtils.toXMLElement(data.source);
                        let prompt: string = MTUtils.getRole(data.srcLang, data.tgtLang) + ' ' + MTUtils.generatePrompt(source, data.srcLang, data.tgtLang, data.terms);
                        clipboard.writeText(prompt);
                        Swordfish.showNotification('Prompt copied to clipboard');
                    }
                } catch (e) {
                    if (e instanceof Error) {
                        Swordfish.showMessage({ type: 'error', message: e.message });
                    }
                }
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static showPromptDialog(prompt: string): void {
        if (this.promptWindow && !this.promptWindow.isDestroyed()) {
            this.promptWindow.webContents.send('set-prompt', prompt);
            this.promptWindow.focus();
        } else {
            this.promptWindow = new BrowserWindow({
                parent: this.mainWindow,
                width: 680,
                height: 560,
                minimizable: false,
                maximizable: false,
                resizable: true,
                show: false,
                icon: this.iconPath,
                webPreferences: {
                    nodeIntegration: true,
                    contextIsolation: false
                }
            });
            this.promptWindow.setMenu(null);
            let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'promptDialog.html');
            let fileUrl: URL = new URL('file://' + filePath);
            this.promptWindow.loadURL(fileUrl.href);
        }
        this.promptWindow.once('ready-to-show', () => {
            this.promptWindow.webContents.send('set-prompt', prompt);
            this.promptWindow.show();
        });
        this.promptWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.promptWindow, 'promptDialog.html');
    }

    static insertAiResponse(): void {
        let clipboardText: string = clipboard.readText();
        try {
            let target: XMLElement = MTUtils.toXMLElement(clipboardText);
            if (target.getName() === 'target') {
                this.mainWindow.webContents.send('insert-ai-response', target.toString());
            } else {
                Swordfish.showMessage({ type: 'error', message: 'Invalid AI response: ' + clipboardText });
                return;
            }
        } catch (e) {
            if (e instanceof Error) {
                Swordfish.showMessage({ type: 'error', message: 'Invalid AI response: ' + clipboardText });
                return;
            }
        }
    }

    static insertResponse(aiResponse: any): void {
        Swordfish.sendRequest('/projects/setTarget', aiResponse,
            (data: any) => {
                if (data.status === Swordfish.SUCCESS) {
                    Swordfish.mainWindow.webContents.send('set-target', data);
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static fixTags(args: any): void {
        if (!(Swordfish.currentPreferences.chatGpt.enabled || Swordfish.currentPreferences.anthropic.enabled)) {
            Swordfish.showMessage({ type: 'error', message: 'No AI engine is currently enabled' });
            return;
        }
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Fixing tags');
        Swordfish.sendRequest('/projects/getSegment', args,
            (data: any) => {
                try {
                    if (data.status !== Swordfish.SUCCESS) {
                        console.error('Swordfish.fixTags', data);
                    } else {
                        let mtManager = new MTManager(Swordfish.currentPreferences, args.srcLang, args.tgtLang);
                        data.project = args.project;
                        data.file = args.file;
                        data.unit = args.unit;
                        data.segment = args.segment;
                        mtManager.fixTags(data);
                    }
                } catch (e) {
                    Swordfish.mainWindow.webContents.send('end-waiting');
                    Swordfish.mainWindow.webContents.send('set-status', '');
                    if (e instanceof Error) {
                        Swordfish.showMessage({ type: 'error', message: e.message });
                    }
                }
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static updateTarget(params: any) {
        Swordfish.mainWindow.webContents.send('update-target-cell', params);
        Swordfish.mainWindow.webContents.send('end-waiting');
        Swordfish.mainWindow.webContents.send('set-status', '');
    }
    static fixMatch(match: Match) {
        if (!(Swordfish.currentPreferences.chatGpt.enabled || Swordfish.currentPreferences.anthropic.enabled)) {
            Swordfish.showMessage({ type: 'error', message: 'No AI engine is currently enabled' });
            return;
        }
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Fixing match');
        Swordfish.sendRequest('/projects/getMatchData', match,
                        (data: any) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                Swordfish.fixMatches(data, match.srcLang, match.tgtLang);
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static exportHTML(arg: any): void {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Exporting HTML');
        Swordfish.sendRequest('/projects/exportHtml', arg,
            (data: any) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                shell.openExternal('file://' + data.export).catch(() => {
                    shell.openPath(data.export).catch((reason: any) => {
                        if (reason instanceof Error) {
                            console.error(reason.message);
                        }
                        this.showMessage({ type: 'error', message: 'Unable to open HTML.' });
                    });
                });
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('end-waiting');
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static showChangeCase(): void {
        this.changeCaseWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 250,
            height: 350,
            minimizable: false,
            maximizable: false,
            resizable: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        this.changeCaseWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'changeCase.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.changeCaseWindow.loadURL(fileUrl.href);
        this.changeCaseWindow.once('ready-to-show', () => {
            let bounds: Rectangle = Swordfish.mainWindow.getBounds();
            if (!Swordfish.locations.hasLocation('changeCase.html')) {
                this.changeCaseWindow.setPosition(bounds.x + Number.parseInt('' + (bounds.width / 5)), bounds.y + Number.parseInt('' + (bounds.height / 4)));
            }
            this.changeCaseWindow.show();
        });
        this.changeCaseWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.changeCaseWindow, 'changeCase.html');
    }

    static changeCaseTo(arg: any) {
        Swordfish.mainWindow.webContents.send('case-changed', arg);
        this.changeCaseWindow.close();
    }

    static splitSegment(arg: any): void {
        Swordfish.sendRequest('/projects/splitSegment', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                Swordfish.mainWindow.webContents.send('count-changed', { project: arg.project });
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static mergeSegment(arg: any): void {
        Swordfish.sendRequest('/projects/mergeSegment', arg,
            (data: any) => {
                if (data.status !== Swordfish.SUCCESS) {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                    return;
                }
                Swordfish.mainWindow.webContents.send('count-changed', { project: arg.project });
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static toggleNotes(): void {
        if (Swordfish.notesWindow) {
            Swordfish.notesWindow.close();
            Swordfish.mainWindow.webContents.send('notes-closed');
            Swordfish.notesParam = undefined;
            return;
        }
        Swordfish.mainWindow.webContents.send('notes-requested');
    }

    static showNotes(arg: any): void {
        if (!Swordfish.notesWindow) {
            Swordfish.notesWindow = new BrowserWindow({
                parent: Swordfish.mainWindow,
                width: 450,
                height: 300,
                minimizable: false,
                maximizable: false,
                resizable: true,
                show: false,
                alwaysOnTop: true,
                icon: this.iconPath,
                webPreferences: {
                    nodeIntegration: true,
                    contextIsolation: false
                }
            });
            Swordfish.notesParam = arg;
            Swordfish.notesWindow.setMenu(null);
            let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'notes.html');
            let fileUrl: URL = new URL('file://' + filePath);
            Swordfish.notesWindow.loadURL(fileUrl.href);
            Swordfish.notesWindow.addListener('closed', () => {
                Swordfish.mainWindow.webContents.send('notes-closed');
            });
            Swordfish.notesWindow.once('ready-to-show', () => {
                Swordfish.notesWindow.show();
            });
            this.notesWindow.on('close', () => {
                this.mainWindow.focus();
            });
            return;
        }
        Swordfish.getNotes(arg);
    }

    static getNotes(arg: any) {
        if (!Swordfish.notesParam) {
            return;
        }
        Swordfish.sendRequest('/projects/getNotes', arg,
            (data: any) => {
                if (data.status === 'Success') {
                    Swordfish.notesEvent.sender.send('note-params', arg);
                    Swordfish.notesEvent.sender.send('set-notes', data);
                            } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                            }
                        },
                        (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static showAddNote(arg: any): void {
        Swordfish.notesParam = arg;
        Swordfish.addNoteWindow = new BrowserWindow({
            parent: Swordfish.notesWindow,
            width: 350,
            height: 180,
            minimizable: false,
            maximizable: false,
            resizable: true,
            modal: true,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        Swordfish.notesParam = arg;
        Swordfish.addNoteWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'addNote.html');
        let fileUrl: URL = new URL('file://' + filePath);
        Swordfish.addNoteWindow.loadURL(fileUrl.href);
        Swordfish.addNoteWindow.once('ready-to-show', () => {
            Swordfish.addNoteWindow.show();
        });
        this.addNoteWindow.on('close', () => {
            this.notesWindow.focus();
        });
        Swordfish.setLocation(this.addNoteWindow, 'addNote.html');
    }

    static addNote(arg: any) {
        Swordfish.addNoteWindow.close();
        Swordfish.sendRequest('/projects/addNote', arg,
            (data: any) => {
                if (data.status === 'Success') {
                    Swordfish.notesEvent.sender.send('note-params', arg);
                    Swordfish.notesEvent.sender.send('set-notes', data);
                    Swordfish.mainWindow.webContents.send('notes-added', arg);
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static removeNote(arg: any) {
        Swordfish.sendRequest('/projects/removeNote', arg,
            (data: any) => {
                if (data.status === 'Success') {
                    Swordfish.notesEvent.sender.send('note-params', arg);
                    Swordfish.notesEvent.sender.send('set-notes', data);
                    if (data.notes.length === 0) {
                        Swordfish.mainWindow.webContents.send('notes-removed', arg);
                    }
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason });
            }
        );
    }

    static downloadLatest(): void {
        let downloadsFolder = app.getPath('downloads');
        let url: URL = new URL(Swordfish.downloadLink);
        let path: string = url.pathname;
        path = path.substring(path.lastIndexOf('/') + 1);
        let file: string = downloadsFolder + (process.platform === 'win32' ? '\\' : '/') + path;
        if (existsSync(file)) {
            unlinkSync(file);
        }
        let request: Electron.ClientRequest = net.request({
            url: Swordfish.downloadLink,
            session: session.defaultSession
        });
        Swordfish.mainWindow.webContents.send('set-status', 'Downloading...');
        Swordfish.updatesWindow.close();
        request.on('response', (response: IncomingMessage) => {
            let fileSize = Number.parseInt(response.headers['content-length'] as string);
            let received: number = 0;
            response.on('data', (chunk: Buffer) => {
                received += chunk.length;
                if (process.platform === 'win32' || process.platform === 'darwin') {
                    Swordfish.mainWindow.setProgressBar(received / fileSize);
                }
                Swordfish.mainWindow.webContents.send('set-status', 'Downloaded: ' + Math.trunc(received * 100 / fileSize) + '%');
                appendFileSync(file, chunk);
            });
            response.on('end', () => {
                Swordfish.mainWindow.webContents.send('set-status', '');
                dialog.showMessageBox({
                    type: 'info',
                    message: 'Update downloaded'
                });
                if (process.platform === 'win32' || process.platform === 'darwin') {
                    Swordfish.mainWindow.setProgressBar(0);
                    shell.openPath(file).then(() => {
                        app.quit();
                    }).catch((reason: string) => {
                        dialog.showErrorBox('Error', reason);
                    });
                }
                if (process.platform === 'linux') {
                    shell.showItemInFolder(file);
                }
            });
            response.on('error', (error: Error) => {
                Swordfish.mainWindow.webContents.send('set-status', '');
                dialog.showErrorBox('Error', error.message);
                if (process.platform === 'win32' || process.platform === 'darwin') {
                    Swordfish.mainWindow.setProgressBar(0);
                }
            });
        });
        request.end();
    }

    static showGettingStarted(): void {
        Swordfish.gettingStartedWindow = new BrowserWindow({
            parent: Swordfish.mainWindow,
            width: 740,
            height: 540,
            minimizable: false,
            maximizable: false,
            resizable: false,
            modal: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        Swordfish.gettingStartedWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'gettingStarted.html');
        let fileUrl: URL = new URL('file://' + filePath);
        Swordfish.gettingStartedWindow.loadURL(fileUrl.href);
        Swordfish.gettingStartedWindow.once('ready-to-show', () => {
            Swordfish.gettingStartedWindow.show();
        });
        this.gettingStartedWindow.on('close', () => {
            this.mainWindow.focus();
        });
        Swordfish.setLocation(this.gettingStartedWindow, 'gettingStarted.html');
    }

    static getXMLFilters(event: IpcMainEvent): void {
        this.sendRequest('/services/xmlFilters', { path: app.getAppPath() },
            (data: any) => {
                if (data.status === 'Success') {
                    event.sender.send('xmlFilters', data);
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason, parent: 'preferences' });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason, parent: 'preferences' });
            }
        );
    }

    static editXmlFilter(arg: any): void {
        Swordfish.xmlFilter = arg.file;
        Swordfish.editXmlFilterWindow = new BrowserWindow({
            parent: Swordfish.preferencesWindow,
            width: 800,
            height: 405,
            minimizable: false,
            maximizable: false,
            resizable: true,
            modal: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        Swordfish.editXmlFilterWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'filterConfig.html');
        let fileUrl: URL = new URL('file://' + filePath);
        Swordfish.editXmlFilterWindow.loadURL(fileUrl.href);
        Swordfish.editXmlFilterWindow.once('ready-to-show', () => {
            Swordfish.editXmlFilterWindow.show();
        });
        this.editXmlFilterWindow.on('close', () => {
            this.preferencesWindow.focus();
        });
        Swordfish.setLocation(this.editXmlFilterWindow, 'filterConfig.html');
    }

    static getXmlFilterData(event: IpcMainEvent): void {
        this.sendRequest('/services/filterData', { path: app.getAppPath(), file: Swordfish.xmlFilter },
            (data: any) => {
                if (data.status === 'Success') {
                    data.filter = Swordfish.xmlFilter;
                    event.sender.send('set-filterData', data);
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason, parent: 'filterConfig' });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason, parent: 'filterConfig' });
            }
        );
    }

    static getElementConfig(event: IpcMainEvent): void {
        event.sender.send('set-elementConfig', Swordfish.filterElement);
    }

    static saveElementConfig(arg: any): void {
        arg.path = app.getAppPath();
        this.sendRequest('/services/saveElement', arg,
            (data: any) => {
                if (data.status === 'Success') {
                    Swordfish.configElementWindow.close();
                    Swordfish.editXmlFilterWindow.webContents.send('refresh');
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason, parent: 'elementConfig' });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason, parent: 'elementConfig' });
            }
        );
    }

    static removeElements(arg: any) {
        arg.path = app.getAppPath();
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Remove selected elements?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                this.sendRequest('/services/removeElements', arg,
                    (data: any) => {
                        if (data.status === 'Success') {
                            Swordfish.editXmlFilterWindow.webContents.send('refresh');
                        } else {
                            Swordfish.showMessage({ type: 'error', message: data.reason, parent: 'filterConfig' });
                        }
                    },
                    (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason, parent: 'filterConfig' });
                    }
                );
            }
        });
    }

    static addElement(arg: any): void {
        Swordfish.filterElement = arg;
        Swordfish.configElementWindow = new BrowserWindow({
            parent: Swordfish.editXmlFilterWindow,
            width: 390,
            height: 310,
            minimizable: false,
            maximizable: false,
            resizable: false,
            modal: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        Swordfish.configElementWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'elementConfig.html');
        let fileUrl: URL = new URL('file://' + filePath);
        Swordfish.configElementWindow.loadURL(fileUrl.href);
        Swordfish.configElementWindow.once('ready-to-show', () => {
            Swordfish.configElementWindow.show();
        });
        this.configElementWindow.on('close', () => {
            this.editXmlFilterWindow.focus();
        });
        Swordfish.setLocation(this.configElementWindow, 'elementConfig.html');
    }

    static importXmlFilter(event: IpcMainEvent): void {
        dialog.showOpenDialog({
            properties: ['openFile'],
            filters: [
                { name: 'XML Document', extensions: ['xml'] }
            ]
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                let selectedFile: string = value.filePaths[0];
                this.sendRequest('/services/importFilter', { path: app.getAppPath(), file: selectedFile },
                    (data: any) => {
                        if (data.status === 'Success') {
                            Swordfish.getXMLFilters(event);
                            this.showMessage({ type: 'info', message: 'Configuration file imported', parent: 'preferences' });
                        } else {
                            Swordfish.showMessage({ type: 'error', message: data.reason, parent: 'preferences' });
                        }
                    },
                    (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason, parent: 'preferences' });
                    }
                );
            } else {
                Swordfish.preferencesWindow.focus();
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    static removeXmlFilters(event: IpcMainEvent, arg: any): void {
        dialog.showMessageBox(Swordfish.mainWindow, {
            type: 'question',
            message: 'Remove selected configuration files?',
            buttons: ['Yes', 'No']
        }).then((selection: Electron.MessageBoxReturnValue) => {
            if (selection.response === 0) {
                this.sendRequest('/services/removeFilters', { path: app.getAppPath(), files: arg.files },
                    (data: any) => {
                        if (data.status === 'Success') {
                            Swordfish.getXMLFilters(event);
                        } else {
                            Swordfish.showMessage({ type: 'error', message: data.reason, parent: 'preferences' });
                        }
                    },
                    (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason, parent: 'preferences' });
                    }
                );
            }
        });
    }

    static exportXmlFilters(arg: any): void {
        dialog.showOpenDialog(Swordfish.mainWindow, {
            title: 'Export XML Filter Configurations',
            properties: ['createDirectory', 'openDirectory']
        }).then((value: Electron.OpenDialogReturnValue) => {
            if (!value.canceled) {
                Swordfish.sendRequest('/services/exportFilters', { path: app.getAppPath(), files: arg.files, folder: value.filePaths[0] },
                    (data: any) => {
                        if (data.status === 'Success') {
                            this.showMessage({ type: 'info', message: 'Configuration files exported', parent: 'preferences' });
                        } else {
                            Swordfish.showMessage({ type: 'error', message: data.reason, parent: 'preferences' });
                        }
                    },
                    (reason: string) => {
                        Swordfish.showMessage({ type: 'error', message: reason });
                    }
                );
            } else {
                Swordfish.preferencesWindow.focus();
            }
        }).catch((error: Error) => {
            console.error(error.message);
        });
    }

    static showAddXmlConfiguration(event: IpcMainEvent): void {
        Swordfish.addConfigurationEvent = event;
        Swordfish.addXmlConfigurationWindow = new BrowserWindow({
            parent: Swordfish.preferencesWindow,
            width: 450,
            height: 150,
            minimizable: false,
            maximizable: false,
            resizable: false,
            modal: false,
            show: false,
            icon: this.iconPath,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });
        Swordfish.addXmlConfigurationWindow.setMenu(null);
        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'addXmlConfiguration.html');
        let fileUrl: URL = new URL('file://' + filePath);
        Swordfish.addXmlConfigurationWindow.loadURL(fileUrl.href);
        Swordfish.addXmlConfigurationWindow.once('ready-to-show', () => {
            Swordfish.addXmlConfigurationWindow.show();
        });
        this.addXmlConfigurationWindow.on('close', () => {
            this.preferencesWindow.focus();
        });
        Swordfish.setLocation(this.addXmlConfigurationWindow, 'addXmlConfiguration.html');
    }

    static addXmlConfiguration(event: IpcMainEvent, arg: any): void {
        arg.path = app.getAppPath();
        Swordfish.sendRequest('/services/addFilter', arg,
            (data: any) => {
                if (data.status === 'Success') {
                    Swordfish.showMessage({ type: 'info', message: 'Configuration added' });
                    Swordfish.getXMLFilters(Swordfish.addConfigurationEvent);
                    Swordfish.addXmlConfigurationWindow.close();
                    Swordfish.preferencesWindow.focus();
                } else {
                    Swordfish.showMessage({ type: 'error', message: data.reason, parent: 'addConfiguration' });
                }
            },
            (reason: string) => {
                Swordfish.showMessage({ type: 'error', message: reason, parent: 'addConfiguration' });
            }
        );
    }

    static setLocation(window: BrowserWindow, key: string): void {
        if (Swordfish.locations.hasLocation(key)) {
            let position: Point | undefined = Swordfish.locations.getLocation(key);
            if (position) {
                window.setPosition(position.x, position.y, true);
            }
        }
        window.addListener('moved', () => {
            let bounds: Rectangle = window.getBounds();
            Swordfish.locations.setLocation(key, bounds.x, bounds.y);
        });
    }

    static startup(): void {
        Swordfish.spellCheckerLanguages = Swordfish.mainWindow.webContents.session.availableSpellCheckerLanguages;
        if (Swordfish.currentPreferences.srcLang === 'none') {
            Swordfish.getDefaultLanguages();
        }
        if (Swordfish.currentPreferences.showGuide === undefined) {
            Swordfish.currentPreferences.showGuide = true;
        }
        if (Swordfish.currentPreferences.showGuide) {
            Swordfish.showGettingStarted();
        }
        if (process.platform === 'darwin' && app.runningUnderARM64Translation) {
            Swordfish.showMessage({
                type: 'warning',
                message: 'You are running a version for Macs with Intel processors on a Mac with Apple chipset.'
            });
        }
        setTimeout(() => {
            Swordfish.checkUpdates(true);
        }, 2000);
    }

    static matchTermsInSource(sourceText: string, allTerms: any[], srcLang?: string, tgtLang?: string): any[] {
        let matchedTerms: any[] = [];
        console.log('[matchTermsInSource] 源文本:', sourceText);
        console.log('[matchTermsInSource] 源文本长度:', sourceText.length);
        console.log('[matchTermsInSource] 术语总数:', allTerms.length);
        console.log('[matchTermsInSource] 源语言:', srcLang);
        console.log('[matchTermsInSource] 目标语言:', tgtLang);
        
        // 详细打印所有术语
        for (let i = 0; i < allTerms.length; i++) {
            let term = allTerms[i];
            console.log(`[matchTermsInSource] 术语${i+1}:`, JSON.stringify(term));
        }
        
        // 检测源文本的语言类型
        let sourceTextIsChinese = /[\u4e00-\u9fff]/.test(sourceText);
        console.log('[matchTermsInSource] 源文本是否包含中文:', sourceTextIsChinese);
        
        for (let term of allTerms) {
            console.log('[matchTermsInSource] 当前检查的术语对象:', JSON.stringify(term));
            
            let matched = false;
            let matchedText = '';
            
            // 智能双向匹配：根据源文本语言类型决定匹配策略
            if (sourceTextIsChinese) {
                // 源文本是中文，优先在术语的source字段中匹配中文
                if (term.source && /[\u4e00-\u9fff]/.test(term.source)) {
                    matched = sourceText.includes(term.source);
                    matchedText = term.source;
                    console.log('[matchTermsInSource] 中文源文本匹配术语source:', term.source, '结果:', matched);
                }
                // 如果source字段没有匹配，尝试在target字段中匹配中文
                if (!matched && term.target && /[\u4e00-\u9fff]/.test(term.target)) {
                    matched = sourceText.includes(term.target);
                    matchedText = term.target;
                    console.log('[matchTermsInSource] 中文源文本匹配术语target:', term.target, '结果:', matched);
                }
            } else {
                // 源文本是英文，优先在术语的source字段中匹配英文
                if (term.source && !/[\u4e00-\u9fff]/.test(term.source)) {
                    let regex = new RegExp('\\b' + this.escapeRegExp(term.source) + '\\b', 'i');
                    matched = regex.test(sourceText);
                    matchedText = term.source;
                    console.log('[matchTermsInSource] 英文源文本匹配术语source:', term.source, '正则:', regex.toString(), '结果:', matched);
                }
                // 如果source字段没有匹配，尝试在target字段中匹配英文
                if (!matched && term.target && !/[\u4e00-\u9fff]/.test(term.target)) {
                    let regex = new RegExp('\\b' + this.escapeRegExp(term.target) + '\\b', 'i');
                    matched = regex.test(sourceText);
                    matchedText = term.target;
                    console.log('[matchTermsInSource] 英文源文本匹配术语target:', term.target, '正则:', regex.toString(), '结果:', matched);
                }
            }
            
            if (matched) {
                console.log('[matchTermsInSource] 匹配成功:', matchedText);
                // 添加匹配的文本信息到术语对象中
                let matchedTerm = { ...term, matchedText: matchedText };
                matchedTerms.push(matchedTerm);
            } else {
                console.log('[matchTermsInSource] 匹配失败: source=', term.source, 'target=', term.target);
            }
        }
        console.log('[matchTermsInSource] 最终匹配数量:', matchedTerms.length);
        return matchedTerms;
    }

    static escapeRegExp(string: string): string {
        return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
    }

    // ============ 术语抽取相关方法 ============

    static showTermExtraction(data: any): void {
        // 保存项目数据
        console.log('showTermExtraction收到数据:', data);
        this.termExtractionProjectData = data;
        console.log('保存后的termExtractionProjectData:', this.termExtractionProjectData);
        
        if (this.termExtractionWindow && !this.termExtractionWindow.isDestroyed()) {
            this.termExtractionWindow.show();
            return;
        }

        this.termExtractionWindow = new BrowserWindow({
            parent: this.mainWindow,
            width: 800,
            height: 700,
            modal: false,  // 改为非模态，可以最小化
            resizable: true,
            minimizable: true,  // 启用最小化
            maximizable: true,  // 启用最大化
            closable: true,     // 启用关闭按钮
            show: false,
            title: '术语抽取',  // 设置窗口标题
            icon: this.iconPath,
            titleBarStyle: process.platform === 'darwin' ? 'default' : undefined,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });

        // 设置窗口菜单为null（不显示菜单栏）
        this.termExtractionWindow.setMenu(null);
        
        // 添加窗口关闭事件处理
        this.termExtractionWindow.on('close', (event) => {
            // 如果正在抽取，询问用户是否确认关闭
            if (!this.termExtractionCancelled && this.termExtractionWindow && !this.termExtractionWindow.isDestroyed()) {
                // 可以在这里添加确认对话框，暂时直接取消抽取
                this.termExtractionCancelled = true;
                console.log('术语抽取窗口关闭，取消抽取任务');
            }
            this.mainWindow.focus();
        });

        // 设置窗口位置和大小管理
        Swordfish.setLocation(this.termExtractionWindow, 'termExtraction.html');

        let filePath = Swordfish.path.join(app.getAppPath(), 'html', 'termExtraction.html');
        let fileUrl: URL = new URL('file://' + filePath);
        this.termExtractionWindow.loadURL(fileUrl.href);
        
        // 等待窗口完全加载后再显示和发送数据
        this.termExtractionWindow.once('ready-to-show', () => {
            console.log('术语抽取窗口ready-to-show，准备显示窗口');
            this.termExtractionWindow.show();
        });
        
        // 等待DOM完全加载后再发送数据
        this.termExtractionWindow.webContents.once('dom-ready', () => {
            console.log('术语抽取窗口DOM加载完成，延迟发送数据');
            // 增加延迟时间，确保JavaScript完全执行
            setTimeout(() => {
                console.log('现在发送数据:', this.termExtractionProjectData);
                this.termExtractionWindow.webContents.send('term-extraction-data', this.termExtractionProjectData);
            }, 500);
        });
    }

    static async startTermExtraction(params: any): Promise<void> {
        try {
            console.log('收到术语抽取请求:', params);
            this.termExtractionCancelled = false;
            // 重置术语去重集合
            this.extractedTermsSet.clear();
            
            // 获取项目的段落数据
            console.log('正在获取项目段落数据...');
            const segments = await this.getProjectSegments(params.projectId);
            if (!segments || segments.length === 0) {
                console.log('项目中没有找到可用的段落');
                this.termExtractionWindow.webContents.send('extraction-error', '项目中没有找到可用的段落');
                return;
            }

            // 检查Deepseek配置
            if (!this.currentPreferences.deepseek?.enabled || !this.currentPreferences.deepseek?.apiKey) {
                console.log('Deepseek配置无效');
                this.termExtractionWindow.webContents.send('extraction-error', '请在偏好设置中启用并配置Deepseek API');
                return;
            }

            console.log(`开始术语抽取，共${segments.length}个段落`);
            
            for (let i = 0; i < segments.length; i++) {
                if (this.termExtractionCancelled) {
                    break;
                }

                const segment = segments[i];
                
                // 发送进度更新
                this.termExtractionWindow.webContents.send('extraction-progress', {
                    current: i + 1,
                    total: segments.length,
                    segment: segment.source
                });

                try {
                    // 调用Deepseek API抽取术语
                    const extractedTerms = await this.extractTermsFromSegment(segment, params.srcLang, params.tgtLang);
                    
                    // 发送抽取到的术语（已在extractTermsFromSegment中进行去重）
                    extractedTerms.forEach(term => {
                        this.termExtractionWindow.webContents.send('term-extracted', term);
                    });

                    // 添加延迟以避免API频率限制
                    await new Promise(resolve => setTimeout(resolve, 1000));
                    
            } catch (error) {
                    console.error('抽取段落术语时出错:', error);
                    // 继续处理下一个段落
                }
            }

            // 抽取完成
            console.log(`术语抽取完成，共抽取了 ${this.extractedTermsSet.size} 个唯一术语`);
            this.termExtractionWindow.webContents.send('extraction-completed', {});
            
        } catch (error) {
            console.error('术语抽取过程中出错:', error);
            this.termExtractionWindow.webContents.send('extraction-error', '术语抽取过程中发生错误');
        }
    }

    static stopTermExtraction(params: any): void {
        this.termExtractionCancelled = true;
        console.log('术语抽取已取消');
    }

    static async getProjectSegments(projectId: string): Promise<any[]> {
        return new Promise((resolve, reject) => {
            console.log('开始获取项目段落数据，项目ID:', projectId);
            this.sendRequest('/projects/segments', { 
                project: projectId,
                start: 0,
                count: 1000, // 获取前1000个段落
                filterText: '',
                filterLanguage: 'source',
                caseSensitiveFilter: false,
                regExp: false,
                showUntranslated: true,
                showTranslated: true,
                showConfirmed: true,
                sortOption: 'none',
                sortDesc: false
            },
                (data: any) => {
                    console.log('获取到段落数据:', data.segments?.length || 0, '个段落');
                    resolve(data.segments || []);
                },
                (reason: string) => {
                    console.error('获取项目段落失败:', reason);
                    resolve([]);
                }
            );
        });
    }

    static async extractTermsFromSegment(segment: any, srcLang: string, tgtLang: string): Promise<any[]> {
        const prompt = this.buildTermExtractionPrompt(segment.source, segment.target, srcLang, tgtLang);
        
        try {
            const response = await this.callDeepSeekAPIForTermExtraction(prompt);
            return this.parseTermExtractionResponse(response, segment.source);
        } catch (error) {
            console.error('Deepseek API调用失败:', error);
            return [];
        }
    }

    static buildTermExtractionPrompt(source: string, target: string, srcLang: string, tgtLang: string): string {
        return `你是一个专业的术语抽取专家。请从以下源文本和目标文本中抽取专业术语对。

源语言: ${srcLang}
目标语言: ${tgtLang}

源文本: "${source}"
目标文本: "${target}"

请按照以下要求抽取术语:
1. 只抽取专业术语、技术术语或特定领域术语
2. 不要抽取常用词汇、代词、介词等
3. 确保源术语和目标术语是对应的翻译关系
4. 术语长度通常在1-4个词之间

请以JSON格式返回结果，格式如下:
{
  "terms": [
    {
      "source": "源术语",
      "target": "目标术语"
    }
  ]
}

如果没有找到合适的术语，请返回空数组。`;
    }

    static async callDeepSeekAPIForTermExtraction(prompt: string): Promise<string> {
        const apiKey = this.currentPreferences.deepseek?.apiKey;
        if (!apiKey) {
            throw new Error('请在偏好设置中配置Deepseek API密钥');
        }

        const messages = [
            {
                role: "user",
                content: prompt
            }
        ];

        const requestData = JSON.stringify({
            model: "deepseek-chat",
            messages: messages,
            response_format: { type: "json_object" },
            temperature: 0.1,
            max_tokens: 1000
        });

        const options = {
            hostname: 'api.deepseek.com',
            path: '/chat/completions',
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${apiKey}`,
                'Content-Length': Buffer.byteLength(requestData)
            }
        };

        return new Promise((resolve, reject) => {
            const req = require('https').request(options, (res: any) => {
                let data = '';

                res.on('data', (chunk: any) => {
                    data += chunk;
                });

                res.on('end', () => {
                    try {
                        const response = JSON.parse(data);
                        
                        if (response.error) {
                            reject(new Error(response.error.message || 'API请求失败'));
                            return;
                        }

                        if (response.choices && response.choices[0] && response.choices[0].message) {
                            resolve(response.choices[0].message.content);
                        } else {
                            reject(new Error('API响应格式错误'));
                        }
                    } catch (error) {
                        reject(new Error('解析API响应失败'));
                    }
                });
            });

            req.on('error', (error: any) => {
                reject(new Error(`网络请求失败: ${error.message}`));
            });

            req.write(requestData);
            req.end();
        });
    }

    static parseTermExtractionResponse(response: string, sourceContext: string): any[] {
        try {
            const parsed = JSON.parse(response);
            const terms = parsed.terms || [];
            
            const filteredTerms = terms.map((term: any) => ({
                source: term.source,
                target: term.target,
                context: sourceContext
            })).filter((term: any) => {
                // 基本验证：确保源术语和目标术语都存在
                if (!term.source || !term.target) {
                    return false;
                }
                
                // 创建术语对的唯一标识符（不区分大小写，去除首尾空格）
                const termKey = `${term.source.trim().toLowerCase()}|||${term.target.trim().toLowerCase()}`;
                
                // 检查是否已经存在相同的术语对
                if (this.extractedTermsSet.has(termKey)) {
                    console.log(`跳过重复术语: ${term.source} -> ${term.target}`);
                    return false;
                }
                
                // 添加到去重集合
                this.extractedTermsSet.add(termKey);
                console.log(`添加新术语: ${term.source} -> ${term.target}`);
                return true;
            });
            
            return filteredTerms;
            
        } catch (error) {
            console.error('解析术语抽取响应失败:', error);
            return [];
        }
    }

    static async addTermsToGlossary(params: any): Promise<void> {
        const { glossaryId, terms } = params;
        console.log('开始添加术语到术语库:', glossaryId, '术语数量:', terms.length);
        
        let successCount = 0;
        let failCount = 0;
        
        // 串行添加术语，避免并发访问数据库
        for (let index = 0; index < terms.length; index++) {
            const term = terms[index];
            console.log(`添加术语 ${index + 1}/${terms.length}:`, term.source, '->', term.target);
            
            try {
                await new Promise<void>((resolve, reject) => {
                    this.sendRequest('/glossaries/addTerm', {
                        glossary: glossaryId,
                        sourceTerm: term.source,
                        targetTerm: term.target,
                        srcLang: this.termExtractionProjectData?.srcLang || 'zh-CN',
                        tgtLang: this.termExtractionProjectData?.tgtLang || 'en-US'
                    },
                    (data: any) => {
                        console.log('术语添加成功:', term.source, '->', term.target);
                        successCount++;
                        resolve();
                    },
                    (reason: string) => {
                        console.error('添加术语失败:', term.source, reason);
                        failCount++;
                        resolve(); // 即使失败也继续处理下一个
                    });
                });
                
                // 添加延迟避免数据库锁定
                if (index < terms.length - 1) {
                    await new Promise(resolve => setTimeout(resolve, 200));
                }
                
            } catch (error) {
                console.error('添加术语异常:', term.source, error);
                failCount++;
            }
        }
        
        console.log(`术语添加完成: 成功 ${successCount} 个, 失败 ${failCount} 个`);
        
        // 通知前端结果
        if (this.termExtractionWindow && !this.termExtractionWindow.isDestroyed()) {
            this.termExtractionWindow.webContents.send('terms-added-result', {
                success: successCount,
                failed: failCount,
                total: terms.length
            });
        }
    }

    static getTermExtractionGlossaries(event: IpcMainEvent): void {
        Swordfish.mainWindow.webContents.send('start-waiting');
        Swordfish.mainWindow.webContents.send('set-status', 'Loading glossaries');
        Swordfish.sendRequest('/glossaries/list', {},
            (data: any) => {
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.mainWindow.webContents.send('end-waiting');
                if (data.status === Swordfish.SUCCESS) {
                    // 筛选出匹配项目语言对的术语库
                    let filteredGlossaries = data.glossaries;
                    if (Swordfish.termExtractionProjectData) {
                        const projectSrcLang = Swordfish.termExtractionProjectData.srcLang;
                        const projectTgtLang = Swordfish.termExtractionProjectData.tgtLang;
                        console.log('筛选术语库，项目语言对:', projectSrcLang, '->', projectTgtLang);
                        // 暂时返回所有术语库，语言匹配逻辑可以后续完善
                        // TODO: 根据术语库内容过滤语言对
                    }
                    event.sender.send('set-term-extraction-glossaries', filteredGlossaries);
                } else {
                    console.error('获取术语库失败:', data.reason);
                }
            },
            (reason: string) => {
                Swordfish.mainWindow.webContents.send('set-status', '');
                Swordfish.mainWindow.webContents.send('end-waiting');
                console.error('获取术语库请求失败:', reason);
            }
        );
    }

    static extractTermsWithDeepseek(params: any): void {
        console.log('开始使用DeepSeek抽取术语:', params);
        // TODO: 实现DeepSeek术语抽取逻辑
    }

    // AI设置相关方法
    static openAISettings(): void {
        if (Swordfish.aiSettingsWindow && !Swordfish.aiSettingsWindow.isDestroyed()) {
            Swordfish.aiSettingsWindow.focus();
            return;
        }

        Swordfish.aiSettingsWindow = new BrowserWindow({
            parent: Swordfish.aiAssistantWindow,
            modal: true,
            width: 700,
            height: 600,
            show: false,
            frame: false,  // 隐藏默认标题栏
            titleBarStyle: 'hidden',
            resizable: true,
            minimizable: true,
            maximizable: true,
            webPreferences: {
                nodeIntegration: true,
                contextIsolation: false
            }
        });

        Swordfish.aiSettingsWindow.loadFile('html/aiSettings.html');
        
        Swordfish.aiSettingsWindow.once('ready-to-show', () => {
            if (Swordfish.aiSettingsWindow && !Swordfish.aiSettingsWindow.isDestroyed()) {
                Swordfish.aiSettingsWindow.show();
            }
        });

        Swordfish.aiSettingsWindow.on('closed', () => {
            Swordfish.aiSettingsWindow = null as any;
        });
    }

    static async testAnythingLLMConnection(event: IpcMainEvent, config: { url: string; apiKey: string }): Promise<void> {
        try {
            const axios = require('axios');
            
            // 创建axios配置，绕过代理访问localhost
            const axiosConfig = {
                headers: {
                    'Authorization': `Bearer ${config.apiKey}`,
                    'Accept': 'application/json'
                },
                timeout: 10000,
                // 绕过代理设置
                proxy: false,
                // 忽略环境变量中的代理设置
                httpsAgent: undefined,
                httpAgent: undefined
            };

            const response = await axios.get(`${config.url}/api/v1/auth`, axiosConfig);

            event.reply('anythingllm-test-result', { success: true });
        } catch (error: any) {
            console.error('AnythingLLM连接测试失败:', error);
            event.reply('anythingllm-test-result', { 
                success: false, 
                error: error.response?.data?.message || error.message || '连接失败'
            });
        }
    }

    static async getAnythingLLMWorkspaces(event: IpcMainEvent, config: { url: string; apiKey: string }): Promise<void> {
        try {
            const axios = require('axios');
            
            // 创建axios配置，绕过代理访问localhost
            const axiosConfig = {
                headers: {
                    'Authorization': `Bearer ${config.apiKey}`,
                    'Accept': 'application/json'
                },
                timeout: 10000,
                // 绕过代理设置
                proxy: false,
                httpsAgent: undefined,
                httpAgent: undefined
            };

            const response = await axios.get(`${config.url}/api/v1/workspaces`, axiosConfig);

            if (response.data && Array.isArray(response.data.workspaces)) {
                event.reply('anythingllm-workspaces', { 
                    success: true, 
                    workspaces: response.data.workspaces 
                });
            } else {
                event.reply('anythingllm-workspaces', { 
                    success: false, 
                    error: '无法获取工作区列表' 
                });
            }
        } catch (error: any) {
            console.error('获取AnythingLLM工作区失败:', error);
            event.reply('anythingllm-workspaces', { 
                success: false, 
                error: error.response?.data?.message || error.message || '获取工作区失败'
            });
        }
    }

    static getAISettings(): any {
        try {
            const fs = require('fs');
            const settingsPath = Swordfish.path.join(Swordfish.appHome, 'ai-settings.json');
            
            if (fs.existsSync(settingsPath)) {
                const data = fs.readFileSync(settingsPath, 'utf8');
                return JSON.parse(data);
            }
        } catch (error) {
            console.error('读取AI设置失败:', error);
        }
        
        // 返回默认设置
        return {
            provider: 'deepseek'
        };
    }

    static saveAISettings(settings: any): boolean {
        try {
            const fs = require('fs');
            const settingsPath = Swordfish.path.join(Swordfish.appHome, 'ai-settings.json');
            fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
            
            // 通知AI助手窗口设置已更新
            if (Swordfish.aiAssistantWindow && !Swordfish.aiAssistantWindow.isDestroyed()) {
                Swordfish.aiAssistantWindow.webContents.send('ai-settings-updated', settings);
            }
            
            return true;
        } catch (error) {
            console.error('保存AI设置失败:', error);
            return false;
        }
    }
}

try {
    new Swordfish();
} catch (e) {
    console.error("Unable to instantiate Swordfish();");
}
